parsql_postgres/crud_ops.rs
1use postgres::{types::ToSql, Client, Error, Row};
2use crate::{SqlQuery, SqlParams, UpdateParams, FromRow};
3
4/// CrudOps trait defines the CRUD (Create, Read, Update, Delete) operations
5/// that can be performed on a PostgreSQL database.
6///
7/// This trait is implemented for the `postgres::Client` struct, allowing
8/// CRUD operations to be called as extension methods on a client.
9///
10/// # Example
11///
12/// ```rust,no_run
13/// use postgres::{Client, NoTls, Error};
14/// use parsql::postgres::CrudOps;
15/// use parsql::macros::{Insertable, SqlParams, Queryable, FromRow};
16///
17/// #[derive(Insertable, SqlParams)]
18/// #[table("users")]
19/// struct InsertUser {
20/// name: String,
21/// email: String,
22/// }
23///
24/// #[derive(Queryable, FromRow, SqlParams)]
25/// #[table("users")]
26/// #[where_clause("id = $1")]
27/// struct GetUser {
28/// id: i32,
29/// name: String,
30/// email: String,
31/// }
32///
33/// fn main() -> Result<(), Error> {
34/// let mut client = Client::connect("host=localhost user=postgres", NoTls)?;
35///
36/// // Extension method for insert
37/// let insert_user = InsertUser {
38/// name: "John".to_string(),
39/// email: "john@example.com".to_string(),
40/// };
41/// let rows_affected = client.insert(insert_user)?;
42///
43/// // Extension method for get
44/// let get_user = GetUser {
45/// id: 1,
46/// name: String::new(),
47/// email: String::new(),
48/// };
49/// let user = client.get(&get_user)?;
50///
51/// println!("User: {:?}", user);
52/// Ok(())
53/// }
54/// ```
55pub trait CrudOps {
56 /// Inserts a new record into the PostgreSQL database.
57 ///
58 /// # Arguments
59 /// * `entity` - Data object to be inserted (must implement SqlQuery and SqlParams traits)
60 ///
61 /// # Returns
62 /// * `Result<u64, Error>` - On success, returns the number of inserted records; on failure, returns Error
63 fn insert<T: SqlQuery + SqlParams>(&mut self, entity: T) -> Result<u64, Error>;
64
65 /// Updates records in the PostgreSQL database.
66 ///
67 /// # Arguments
68 /// * `entity` - Data object containing the update information (must implement SqlQuery and UpdateParams traits)
69 ///
70 /// # Returns
71 /// * `Result<u64, Error>` - On success, returns the number of updated records; on failure, returns Error
72 fn update<T: SqlQuery + UpdateParams>(&mut self, entity: T) -> Result<u64, Error>;
73
74 /// Deletes records from the PostgreSQL database.
75 ///
76 /// # Arguments
77 /// * `entity` - Data object containing delete conditions (must implement SqlQuery and SqlParams traits)
78 ///
79 /// # Returns
80 /// * `Result<u64, Error>` - On success, returns the number of deleted records; on failure, returns Error
81 fn delete<T: SqlQuery + SqlParams>(&mut self, entity: T) -> Result<u64, Error>;
82
83 /// Retrieves a single record from the PostgreSQL database.
84 ///
85 /// # Arguments
86 /// * `entity` - Data object containing query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
87 ///
88 /// # Returns
89 /// * `Result<T, Error>` - On success, returns the retrieved record; on failure, returns Error
90 fn get<T: SqlQuery + FromRow + SqlParams>(&mut self, entity: &T) -> Result<T, Error>;
91
92 /// Retrieves multiple records from the PostgreSQL database.
93 ///
94 /// # Arguments
95 /// * `entity` - Data object containing query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
96 ///
97 /// # Returns
98 /// * `Result<Vec<T>, Error>` - On success, returns a vector of records; on failure, returns Error
99 fn get_all<T: SqlQuery + FromRow + SqlParams>(&mut self, entity: &T) -> Result<Vec<T>, Error>;
100
101 /// Executes a custom query and transforms the result using the provided function.
102 ///
103 /// # Arguments
104 /// * `entity` - Data object containing query parameters (must implement SqlQuery and SqlParams traits)
105 /// * `to_model` - Function to transform the database row into the desired type
106 ///
107 /// # Returns
108 /// * `Result<R, Error>` - On success, returns the transformed result; on failure, returns Error
109 fn select<T, F, R>(&mut self, entity: &T, to_model: F) -> Result<R, Error>
110 where
111 T: SqlQuery + SqlParams,
112 F: FnOnce(&Row) -> Result<R, Error>;
113
114 /// Executes a custom query and transforms all results using the provided function.
115 ///
116 /// # Arguments
117 /// * `entity` - Data object containing query parameters (must implement SqlQuery and SqlParams traits)
118 /// * `to_model` - Function to transform database rows into the desired type
119 ///
120 /// # Returns
121 /// * `Result<Vec<R>, Error>` - On success, returns a vector of transformed results; on failure, returns Error
122 fn select_all<T, F, R>(&mut self, entity: &T, to_model: F) -> Result<Vec<R>, Error>
123 where
124 T: SqlQuery + SqlParams,
125 F: FnMut(&Row) -> Result<R, Error>;
126}
127
128// CrudOps trait implementasyonu postgres::Client için
129impl CrudOps for Client {
130 fn insert<T: SqlQuery + SqlParams>(&mut self, entity: T) -> Result<u64, Error> {
131 insert(self, entity)
132 }
133
134 fn update<T: SqlQuery + UpdateParams>(&mut self, entity: T) -> Result<u64, Error> {
135 update(self, entity)
136 }
137
138 fn delete<T: SqlQuery + SqlParams>(&mut self, entity: T) -> Result<u64, Error> {
139 delete(self, entity)
140 }
141
142 fn get<T: SqlQuery + FromRow + SqlParams>(&mut self, entity: &T) -> Result<T, Error> {
143 get(self, entity)
144 }
145
146 fn get_all<T: SqlQuery + FromRow + SqlParams>(&mut self, entity: &T) -> Result<Vec<T>, Error> {
147 get_all(self, entity)
148 }
149
150 fn select<T, F, R>(&mut self, entity: &T, to_model: F) -> Result<R, Error>
151 where
152 T: SqlQuery + SqlParams,
153 F: FnOnce(&Row) -> Result<R, Error>,
154 {
155 let sql = T::query();
156
157 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
158 println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
159 }
160
161 let params = entity.params();
162 let row = self.query_one(&sql, ¶ms)?;
163 to_model(&row)
164 }
165
166 fn select_all<T, F, R>(&mut self, entity: &T, to_model: F) -> Result<Vec<R>, Error>
167 where
168 T: SqlQuery + SqlParams,
169 F: FnMut(&Row) -> Result<R, Error>,
170 {
171 let sql = T::query();
172
173 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
174 println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
175 }
176
177 let params = entity.params();
178 let rows = self.query(&sql, ¶ms)?;
179
180 rows.iter().map(to_model).collect()
181 }
182}
183
184/// # insert
185///
186/// Inserts a new record into the database.
187///
188/// ## Parameters
189/// - `client`: Database connection client
190/// - `entity`: Data object to be inserted (must implement SqlQuery and SqlParams traits)
191///
192/// ## Return Value
193/// - `Result<u64, Error>`: On success, returns the number of inserted records; on failure, returns Error
194///
195/// ## Struct Definition
196/// Structs used with this function should be annotated with the following derive macros:
197///
198/// ```rust,no_run
199/// #[derive(Insertable, SqlParams)] // Required macros
200/// #[table("table_name")] // Table name to insert into
201/// pub struct MyEntity {
202/// pub field1: String,
203/// pub field2: i32,
204/// // ...
205/// }
206/// ```
207///
208/// - `Insertable`: Automatically generates SQL INSERT statements
209/// - `SqlParams`: Automatically generates SQL parameters
210/// - `#[table("table_name")]`: Specifies the table name for the insertion
211///
212/// ## Example Usage
213/// ```rust,no_run
214/// use postgres::{Client, NoTls, Error};
215/// use parsql::postgres::insert;
216///
217/// #[derive(Insertable, SqlParams)]
218/// #[table("users")]
219/// pub struct InsertUser {
220/// pub name: String,
221/// pub email: String,
222/// pub state: i16,
223/// }
224///
225/// fn main() -> Result<(), Error> {
226/// let mut client = Client::connect(
227/// "host=localhost user=postgres dbname=test",
228/// NoTls,
229/// )?;
230///
231/// let insert_user = InsertUser {
232/// name: "John".to_string(),
233/// email: "john@example.com".to_string(),
234/// state: 1_i16,
235/// };
236///
237/// let insert_result = insert(&mut client, insert_user)?;
238/// println!("Insert result: {:?}", insert_result);
239/// Ok(())
240/// }
241/// ```
242pub fn insert<T: SqlQuery + SqlParams>(client: &mut Client, entity: T) -> Result<u64, Error> {
243 let sql = T::query();
244 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
245 println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
246 }
247
248 let params = entity.params();
249 client.execute(&sql, ¶ms)
250}
251
252/// # update
253///
254/// Updates an existing record in the database.
255///
256/// ## Parameters
257/// - `client`: Database connection client
258/// - `entity`: Data object containing the update information (must implement SqlQuery and UpdateParams traits)
259///
260/// ## Return Value
261/// - `Result<u64, Error>`: On success, returns the number of updated records; on failure, returns Error
262///
263/// ## Struct Definition
264/// Structs used with this function should be annotated with the following derive macros:
265///
266/// ```rust,no_run
267/// #[derive(Updateable, UpdateParams)] // Required macros
268/// #[table("table_name")] // Table name to update
269/// #[update("field1, field2")] // Fields to update (optional)
270/// #[where_clause("id = $")] // Update condition
271/// pub struct MyEntity {
272/// pub id: i32, // Fields used in the condition
273/// pub field1: String, // Fields to be updated
274/// pub field2: i32, // Fields to be updated
275/// // ...
276/// }
277/// ```
278///
279/// - `Updateable`: Automatically generates SQL UPDATE statements
280/// - `UpdateParams`: Automatically generates update parameters
281/// - `#[table("table_name")]`: Specifies the table name for the update
282/// - `#[update("field1, field2")]`: Specifies which fields should be updated (if omitted, all fields will be updated)
283/// - `#[where_clause("id = $")]`: Specifies the update condition (`$` will be replaced with parameter value)
284///
285/// ## Example Usage
286/// ```rust,no_run
287/// use postgres::{Client, NoTls, Error};
288/// use parsql::postgres::update;
289///
290/// #[derive(Updateable, UpdateParams)]
291/// #[table("users")]
292/// #[update("name, email")]
293/// #[where_clause("id = $")]
294/// pub struct UpdateUser {
295/// pub id: i32,
296/// pub name: String,
297/// pub email: String,
298/// pub state: i16, // This field won't be updated as it's not specified in the update attribute
299/// }
300///
301/// fn main() -> Result<(), Error> {
302/// let mut client = Client::connect(
303/// "host=localhost user=postgres dbname=test",
304/// NoTls,
305/// )?;
306///
307/// let update_user = UpdateUser {
308/// id: 1,
309/// name: String::from("John"),
310/// email: String::from("john@example.com"),
311/// state: 2,
312/// };
313///
314/// let update_result = update(&mut client, update_user)?;
315/// println!("Update result: {:?}", update_result);
316/// Ok(())
317/// }
318/// ```
319pub fn update<T: SqlQuery + UpdateParams>(
320 client: &mut postgres::Client,
321 entity: T,
322) -> Result<u64, Error> {
323 let sql = T::query();
324 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
325 println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
326 }
327
328 let params = entity.params();
329 match client.execute(&sql, ¶ms) {
330 Ok(rows_affected) => Ok(rows_affected),
331 Err(e) => Err(e),
332 }
333}
334
335/// # delete
336///
337/// Deletes a record from the database.
338///
339/// ## Parameters
340/// - `client`: Database connection client
341/// - `entity`: Data object containing the deletion information (must implement SqlQuery and SqlParams traits)
342///
343/// ## Return Value
344/// - `Result<u64, Error>`: On success, returns the number of deleted records; on failure, returns Error
345///
346/// ## Struct Definition
347/// Structs used with this function should be annotated with the following derive macros:
348///
349/// ```rust,no_run
350/// #[derive(Deletable, SqlParams)] // Required macros
351/// #[table("table_name")] // Table name to delete from
352/// #[where_clause("id = $")] // Delete condition
353/// pub struct MyEntity {
354/// pub id: i32, // Fields used in the condition
355/// // Other fields can be added, but typically only condition fields are necessary
356/// }
357/// ```
358///
359/// - `Deletable`: Automatically generates SQL DELETE statements
360/// - `SqlParams`: Automatically generates SQL parameters
361/// - `#[table("table_name")]`: Specifies the table name for the deletion
362/// - `#[where_clause("id = $")]`: Specifies the delete condition (`$` will be replaced with parameter value)
363///
364/// ## Example Usage
365/// ```rust,no_run
366/// use postgres::{Client, NoTls, Error};
367/// use parsql::postgres::delete;
368///
369/// #[derive(Deletable, SqlParams)]
370/// #[table("users")]
371/// #[where_clause("id = $")]
372/// pub struct DeleteUser {
373/// pub id: i32,
374/// }
375///
376/// fn main() -> Result<(), Error> {
377/// let mut client = Client::connect(
378/// "host=localhost user=postgres dbname=test",
379/// NoTls,
380/// )?;
381///
382/// let delete_user = DeleteUser { id: 6 };
383/// let delete_result = delete(&mut client, delete_user)?;
384///
385/// println!("Delete result: {:?}", delete_result);
386/// Ok(())
387/// }
388/// ```
389pub fn delete<T: SqlQuery + SqlParams>(
390 client: &mut postgres::Client,
391 entity: T,
392) -> Result<u64, Error> {
393 let sql = T::query();
394 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
395 println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
396 }
397
398 let params = entity.params();
399 match client.execute(&sql, ¶ms) {
400 Ok(rows_affected) => Ok(rows_affected),
401 Err(e) => Err(e),
402 }
403}
404
405/// # get
406///
407/// Retrieves a single record from the database.
408///
409/// ## Parameters
410/// - `client`: Database connection client
411/// - `params`: Query parameter object (must implement SqlQuery, FromRow, and SqlParams traits)
412///
413/// ## Return Value
414/// - `Result<T, Error>`: On success, returns the found record; on failure, returns Error
415///
416/// ## Struct Definition
417/// Structs used with this function should be annotated with the following derive macros:
418///
419/// ```rust,no_run
420/// #[derive(Queryable, SqlParams, FromRow, Debug)] // Required macros
421/// #[table("table_name")] // Table name to query
422/// #[where_clause("id = $")] // Query condition
423/// pub struct MyEntity {
424/// pub id: i32, // Field used in the query condition
425/// pub field1: String, // Fields to be populated from the result set
426/// pub field2: i32,
427/// // ...
428/// }
429///
430/// // A factory method is also useful
431/// impl MyEntity {
432/// pub fn new(id: i32) -> Self {
433/// Self {
434/// id,
435/// field1: String::default(),
436/// field2: 0,
437/// // ...
438/// }
439/// }
440/// }
441/// ```
442///
443/// - `Queryable`: Automatically generates SQL SELECT statements
444/// - `SqlParams`: Automatically generates SQL parameters
445/// - `FromRow`: Enables conversion from database row to struct object
446/// - `#[table("table_name")]`: Specifies the table name for the query
447/// - `#[where_clause("id = $")]`: Specifies the query condition (`$` will be replaced with parameter value)
448///
449/// ## Example Usage
450/// ```rust,no_run
451/// use postgres::{Client, NoTls, Error};
452/// use parsql::postgres::get;
453///
454/// #[derive(Queryable, SqlParams, FromRow, Debug)]
455/// #[table("users")]
456/// #[where_clause("id = $")]
457/// pub struct GetUser {
458/// pub id: i32,
459/// pub name: String,
460/// pub email: String,
461/// pub state: i16,
462/// }
463///
464/// impl GetUser {
465/// pub fn new(id: i32) -> Self {
466/// Self {
467/// id,
468/// name: String::default(),
469/// email: String::default(),
470/// state: 0,
471/// }
472/// }
473/// }
474///
475/// fn main() -> Result<(), Error> {
476/// let mut client = Client::connect(
477/// "host=localhost user=postgres dbname=test",
478/// NoTls,
479/// )?;
480///
481/// let get_user = GetUser::new(1);
482/// let get_result = get(&mut client, &get_user)?;
483///
484/// println!("Get result: {:?}", get_result);
485/// Ok(())
486/// }
487/// ```
488pub fn get<T: SqlQuery + FromRow + SqlParams>(
489 client: &mut Client,
490 params: &T,
491) -> Result<T, Error> {
492 let sql = T::query();
493 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
494 println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
495 }
496
497 let params = params.params();
498 match client.query_one(&sql, ¶ms) {
499 Ok(_row) => T::from_row(&_row),
500 Err(e) => Err(e),
501 }
502}
503
504/// # get_all
505///
506/// Retrieves multiple records from the database.
507///
508/// ## Parameters
509/// - `client`: Database connection client
510/// - `params`: Query parameter object (must implement SqlQuery, FromRow, and SqlParams traits)
511///
512/// ## Return Value
513/// - `Result<Vec<T>, Error>`: On success, returns the list of found records; on failure, returns Error
514///
515/// ## Struct Definition
516/// Structs used with this function should be annotated with the following derive macros:
517///
518/// ```rust,no_run
519/// #[derive(Queryable, SqlParams, FromRow, Debug)] // Required macros
520/// #[table("table_name")] // Table name to query
521/// #[select("field1, field2, COUNT(*) as count")] // Custom SELECT statement (optional)
522/// #[join("INNER JOIN other_table ON ...")] // JOIN statements (optional)
523/// #[where_clause("status > $")] // Query condition
524/// #[group_by("field1, field2")] // GROUP BY statement (optional)
525/// #[having("COUNT(*) > 0")] // HAVING statement (optional)
526/// #[order_by("count DESC")] // ORDER BY statement (optional)
527/// pub struct MyEntity {
528/// pub status: i32, // Field used in the query condition
529/// pub field1: String, // Fields to be populated from the result set
530/// pub field2: i32,
531/// pub count: i64, // Calculated value
532/// // ...
533/// }
534///
535/// impl MyEntity {
536/// pub fn new(status: i32) -> Self {
537/// Self {
538/// status,
539/// field1: String::default(),
540/// field2: 0,
541/// count: 0,
542/// // ...
543/// }
544/// }
545/// }
546/// ```
547///
548/// - `Queryable`: Automatically generates SQL SELECT statements
549/// - `SqlParams`: Automatically generates SQL parameters
550/// - `FromRow`: Enables conversion from database row to struct object
551/// - `#[table("table_name")]`: Specifies the table name for the query
552/// - `#[select("...")]`: Creates a custom SELECT statement (if omitted, all fields will be selected)
553/// - `#[join("...")]`: Specifies JOIN statements (can be used multiple times)
554/// - `#[where_clause("...")]`: Specifies the query condition (`$` will be replaced with parameter value)
555/// - `#[group_by("...")]`: Specifies the GROUP BY statement
556/// - `#[having("...")]`: Specifies the HAVING statement
557/// - `#[order_by("...")]`: Specifies the ORDER BY statement
558///
559/// ## Example Usage
560/// ```rust,no_run
561/// use postgres::{Client, NoTls, Error};
562/// use parsql::postgres::get_all;
563///
564/// // Simple query example
565/// #[derive(Queryable, SqlParams, FromRow, Debug)]
566/// #[table("users")]
567/// #[where_clause("email = $")]
568/// pub struct GetAllUsers {
569/// pub id: i32,
570/// pub name: String,
571/// pub email: String,
572/// pub state: i16,
573/// }
574///
575/// // Complex JOIN example
576/// #[derive(Queryable, SqlParams, FromRow, Debug)]
577/// #[table("users")]
578/// #[select("users.id, users.name, users.email, users.state as user_state, posts.id as post_id, posts.content, posts.state as post_state, comments.content as comment")]
579/// #[join("INNER JOIN posts ON users.id = posts.user_id")]
580/// #[join("LEFT JOIN comments ON posts.id = comments.post_id")]
581/// #[where_clause("users.id = $")]
582/// pub struct SelectUserWithPosts {
583/// pub id: i32,
584/// pub name: String,
585/// pub email: String,
586/// pub user_state: i16,
587/// pub post_id: i32,
588/// pub content: String,
589/// pub post_state: i16,
590/// pub comment: Option<String>,
591/// }
592///
593/// // GROUP BY and ORDER BY example
594/// #[derive(Queryable, SqlParams, FromRow, Debug)]
595/// #[table("users")]
596/// #[select("users.state, COUNT(*) as user_count")]
597/// #[where_clause("state > $")]
598/// #[group_by("users.state")]
599/// #[order_by("user_count DESC")]
600/// pub struct UserStateStats {
601/// pub state: i16,
602/// pub user_count: i64,
603/// }
604///
605/// // HAVING filter example
606/// #[derive(Queryable, SqlParams, FromRow, Debug)]
607/// #[table("users")]
608/// #[select("users.state, COUNT(*) as user_count")]
609/// #[where_clause("state > $")]
610/// #[group_by("users.state")]
611/// #[having("COUNT(*) > 1")]
612/// #[order_by("user_count DESC")]
613/// pub struct UserStateStatsFiltered {
614/// pub state: i16,
615/// pub user_count: i64,
616/// }
617///
618/// fn main() -> Result<(), Error> {
619/// let mut client = Client::connect(
620/// "host=localhost user=postgres dbname=test",
621/// NoTls,
622/// )?;
623///
624/// // Example usage
625/// let select_user_with_posts = SelectUserWithPosts::new(1);
626/// let get_user_with_posts = get_all(&mut client, &select_user_with_posts)?;
627///
628/// println!("Get user with posts: {:?}", get_user_with_posts);
629///
630/// // Other examples
631/// let user_state_stats = get_all(&mut client, &UserStateStats::new(0))?;
632/// println!("User state stats: {:?}", user_state_stats);
633///
634/// let user_state_stats_filtered = get_all(&mut client, &UserStateStatsFiltered::new(0))?;
635/// println!("User state stats (filtered with HAVING): {:?}", user_state_stats_filtered);
636/// Ok(())
637/// }
638/// ```
639pub fn get_all<T: SqlQuery + FromRow + SqlParams>(
640 client: &mut Client,
641 params: &T,
642) -> Result<Vec<T>, Error> {
643 let sql = T::query();
644 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
645 println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
646 }
647 let params = params.params();
648 let rows = client.query(&sql, ¶ms)?;
649
650 rows.iter()
651 .map(|row| T::from_row(row))
652 .collect::<Result<Vec<_>, _>>()
653}
654
655/// # get_by_query
656///
657/// Retrieves multiple records from the database using a custom SQL query.
658///
659/// ## Parameters
660/// - `client`: Database connection client
661/// - `query`: Custom SQL query string
662/// - `params`: Array of query parameters
663///
664/// ## Return Value
665/// - `Result<Vec<T>, Error>`: On success, returns the list of found records; on failure, returns Error
666///
667/// ## Example Usage
668/// ```rust,no_run
669/// use postgres::{Client, NoTls, Error};
670/// use parsql::postgres::get_by_query;
671///
672/// #[derive(FromRow, Debug)]
673/// pub struct UserStats {
674/// pub state: i16,
675/// pub user_count: i64,
676/// }
677///
678/// fn main() -> Result<(), Error> {
679/// let mut client = Client::connect(
680/// "host=localhost user=postgres dbname=test",
681/// NoTls,
682/// )?;
683///
684/// let query = "SELECT state, COUNT(*) as user_count FROM users GROUP BY state HAVING COUNT(*) > $1";
685/// let min_count = 5;
686///
687/// let stats = get_by_query::<UserStats>(&mut client, query, &[&min_count])?;
688/// println!("User stats: {:?}", stats);
689/// Ok(())
690/// }
691/// ```
692pub fn get_by_query<T: FromRow>(
693 client: &mut Client,
694 query: &str,
695 params: &[&(dyn ToSql + Sync)],
696) -> Result<Vec<T>, Error> {
697 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
698 println!("[PARSQL-POSTGRES] Execute SQL: {}", query);
699 }
700
701 let rows = client.query(query, params)?;
702 rows.iter()
703 .map(|row| T::from_row(row))
704 .collect::<Result<Vec<_>, _>>()
705}
706
707/// # select
708///
709/// Retrieves a single record from the database using a custom transformation function.
710/// This is useful when you want to use a custom transformation function instead of the FromRow trait.
711///
712/// ## Parameters
713/// - `client`: Database connection client
714/// - `entity`: Query parameter object (must implement SqlQuery and SqlParams traits)
715/// - `to_model`: Function to convert a Row object to the target object type
716///
717/// ## Return Value
718/// - `Result<T, Error>`: On success, returns the transformed object; on failure, returns Error
719///
720/// ## Struct Definition
721/// Structs used with this function should be annotated with the following derive macros:
722///
723/// ```rust,no_run
724/// #[derive(Queryable, SqlParams)] // Required macros (FromRow is not needed)
725/// #[table("table_name")] // Table name to query
726/// #[where_clause("id = $")] // Query condition
727/// pub struct MyQueryEntity {
728/// pub id: i32, // Field used in the query condition
729/// // Other fields can be added if necessary for the query condition
730/// }
731///
732/// // A separate struct can be used for the return value
733/// pub struct MyResultEntity {
734/// pub id: i32,
735/// pub name: String,
736/// pub count: i64,
737/// }
738/// ```
739///
740/// - `Queryable`: Automatically generates SQL SELECT statements
741/// - `SqlParams`: Automatically generates SQL parameters
742/// - `#[table("table_name")]`: Specifies the table name for the query
743/// - `#[where_clause("id = $")]`: Specifies the query condition (`$` will be replaced with parameter value)
744///
745/// ## Example Usage
746/// ```rust,no_run
747/// use postgres::{Client, NoTls, Error};
748/// use parsql::postgres::select;
749///
750/// #[derive(Queryable, SqlParams)]
751/// #[table("users")]
752/// #[where_clause("id = $")]
753/// pub struct UserQuery {
754/// pub id: i32,
755/// }
756///
757/// impl UserQuery {
758/// pub fn new(id: i32) -> Self {
759/// Self { id }
760/// }
761/// }
762///
763/// // Different return structure
764/// pub struct User {
765/// pub id: i32,
766/// pub name: String,
767/// }
768///
769/// fn main() -> Result<(), Error> {
770/// let mut client = Client::connect(
771/// "host=localhost user=postgres dbname=test",
772/// NoTls,
773/// )?;
774///
775/// // A custom model transformation function is required
776/// let user_query = UserQuery::new(1);
777/// let user = select(&mut client, user_query, |row| {
778/// let id: i32 = row.get("id");
779/// let name: String = row.get("name");
780/// Ok(User { id, name })
781/// })?;
782///
783/// println!("User: {:?}", user);
784/// Ok(())
785/// }
786/// ```
787pub fn select<T: SqlQuery + SqlParams, F>(
788 client: &mut postgres::Client,
789 entity: T,
790 to_model: F,
791) -> Result<T, Error>
792where
793 F: Fn(&Row) -> Result<T, Error>,
794{
795 let sql = T::query();
796 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
797 println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
798 }
799
800 let params = entity.params();
801
802 match client.query_one(&sql, ¶ms) {
803 Ok(_row) => to_model(&_row),
804 Err(e) => Err(e),
805 }
806}
807
808/// # select_all
809///
810/// Retrieves multiple records from the database using a custom transformation function.
811/// This is useful when you want to use a custom transformation function instead of the FromRow trait.
812///
813/// ## Parameters
814/// - `client`: Database connection client
815/// - `entity`: Query parameter object (must implement SqlQuery and SqlParams traits)
816/// - `to_model`: Function to convert a Row object to the target object type
817///
818/// ## Return Value
819/// - `Result<Vec<T>, Error>`: On success, returns the list of transformed objects; on failure, returns Error
820///
821/// ## Struct Definition
822/// Structs used with this function should be annotated with the following derive macros:
823///
824/// ```rust,no_run
825/// #[derive(Queryable, SqlParams)] // Required macros (FromRow is not needed)
826/// #[table("table_name")] // Table name to query
827/// #[select("id, name, COUNT(*) as count")] // Custom SELECT statement (optional)
828/// #[where_clause("active = $")] // Query condition
829/// pub struct MyQueryEntity {
830/// pub active: bool, // Field used in the query condition
831/// // Other fields can be added if necessary for the query condition
832/// }
833///
834/// // A separate struct can be used for the return value
835/// pub struct MyResultEntity {
836/// pub id: i32,
837/// pub name: String,
838/// pub count: i64,
839/// }
840/// ```
841///
842/// - `Queryable`: Automatically generates SQL SELECT statements
843/// - `SqlParams`: Automatically generates SQL parameters
844/// - `#[table("table_name")]`: Specifies the table name for the query
845/// - `#[select("...")]`: Creates a custom SELECT statement (if omitted, all fields will be selected)
846/// - `#[where_clause("active = $")]`: Specifies the query condition (`$` will be replaced with parameter value)
847///
848/// ## Example Usage
849/// ```rust,no_run
850/// use postgres::{Client, NoTls, Error};
851/// use parsql::postgres::select_all;
852///
853/// #[derive(Queryable, SqlParams)]
854/// #[table("users")]
855/// #[select("id, name, email")]
856/// pub struct UsersQuery {
857/// // Can be empty for a parameterless query
858/// }
859///
860/// impl UsersQuery {
861/// pub fn new() -> Self {
862/// Self {}
863/// }
864/// }
865///
866/// // Different return structure
867/// pub struct User {
868/// pub id: i32,
869/// pub name: String,
870/// }
871///
872/// fn main() -> Result<(), Error> {
873/// let mut client = Client::connect(
874/// "host=localhost user=postgres dbname=test",
875/// NoTls,
876/// )?;
877///
878/// // A custom model transformation function is required
879/// let users_query = UsersQuery::new();
880/// let users = select_all(&mut client, users_query, |row| {
881/// let id: i32 = row.get("id");
882/// let name: String = row.get("name");
883/// User { id, name }
884/// })?;
885///
886/// println!("Users: {:?}", users);
887/// Ok(())
888/// }
889/// ```
890pub fn select_all<T: SqlQuery + SqlParams, F>(
891 client: &mut postgres::Client,
892 entity: T,
893 to_model: F,
894) -> Result<Vec<T>, Error>
895where
896 F: Fn(&Row) -> Result<T, Error>,
897{
898 let sql = T::query();
899 if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
900 println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
901 }
902
903 let params = entity.params();
904
905 let rows = client.query(&sql, ¶ms)?;
906
907 rows.iter()
908 .map(|row| to_model(row))
909 .collect::<Result<Vec<_>, _>>()
910}