parsql_postgres/
crud_ops.rs

1use crate::traits::{CrudOps, FromRow, SqlCommand, SqlParams, SqlQuery, UpdateParams};
2use postgres::{
3    types::{FromSql, ToSql},
4    Client, Error, Row,
5};
6
7// CrudOps trait implementasyonu postgres::Client için
8impl CrudOps for Client {
9    fn insert<T: SqlCommand + SqlParams, P: for<'a> FromSql<'a> + Send + Sync>(
10        &mut self,
11        entity: T,
12    ) -> Result<P, Error> {
13        insert::<T, P>(self, entity)
14    }
15
16    fn update<T: SqlCommand + UpdateParams>(&mut self, entity: T) -> Result<u64, Error> {
17        update(self, entity)
18    }
19
20    fn delete<T: SqlCommand + SqlParams>(&mut self, entity: T) -> Result<u64, Error> {
21        delete(self, entity)
22    }
23
24    fn fetch<P, R>(&mut self, params: &P) -> Result<R, Error>
25    where
26        P: SqlQuery<R> + SqlParams,
27        R: FromRow,
28    {
29        fetch(self, params)
30    }
31
32    fn fetch_all<P, R>(&mut self, params: &P) -> Result<Vec<R>, Error>
33    where
34        P: SqlQuery<R> + SqlParams,
35        R: FromRow,
36    {
37        fetch_all(self, params)
38    }
39
40    fn select<T, F, R>(&mut self, entity: &T, to_model: F) -> Result<R, Error>
41    where
42        T: SqlQuery<T> + SqlParams,
43        F: FnOnce(&Row) -> Result<R, Error>,
44    {
45        let sql = T::query();
46
47        if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
48            println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
49        }
50
51        let params = entity.params();
52        let row = self.query_one(&sql, &params)?;
53        to_model(&row)
54    }
55
56    fn select_all<T, F, R>(&mut self, entity: &T, to_model: F) -> Result<Vec<R>, Error>
57    where
58        T: SqlQuery<T> + SqlParams,
59        F: FnMut(&Row) -> Result<R, Error>,
60    {
61        let sql = T::query();
62
63        if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
64            println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
65        }
66
67        let params = entity.params();
68        let rows = self.query(&sql, &params)?;
69
70        rows.iter().map(to_model).collect()
71    }
72}
73
74/// # insert
75///
76/// Inserts a new record into the database.
77///
78/// ## Parameters
79/// - `client`: Database connection client
80/// - `entity`: Data object to be inserted (must implement SqlQuery and SqlParams traits)
81///
82/// ## Return Value
83/// - `Result<u64, Error>`: On success, returns the number of inserted records; on failure, returns Error
84///
85/// ## Struct Definition
86/// Structs used with this function should be annotated with the following derive macros:
87///
88/// ```rust,no_run
89/// #[derive(Insertable, SqlParams)]  // Required macros
90/// #[table("table_name")]            // Table name to insert into
91/// pub struct MyEntity {
92///     pub field1: String,
93///     pub field2: i32,
94///     // ...
95/// }
96/// ```
97///
98/// - `Insertable`: Automatically generates SQL INSERT statements
99/// - `SqlParams`: Automatically generates SQL parameters
100/// - `#[table("table_name")]`: Specifies the table name for the insertion
101///
102/// ## Example Usage
103/// ```rust,no_run
104/// use postgres::{Client, NoTls, Error};
105/// use parsql::postgres::insert;
106///
107/// #[derive(Insertable, SqlParams)]
108/// #[table("users")]
109/// pub struct InsertUser {
110///     pub name: String,
111///     pub email: String,
112///     pub state: i16,
113/// }
114///
115/// fn main() -> Result<(), Error> {
116///     let mut client = Client::connect(
117///         "host=localhost user=postgres dbname=test",
118///         NoTls,
119///     )?;
120///
121///     let insert_user = InsertUser {
122///         name: "John".to_string(),
123///         email: "john@example.com".to_string(),
124///         state: 1_i16,
125///     };
126///
127///     let insert_result = insert(&mut client, insert_user)?;
128///     println!("Insert result: {:?}", insert_result);
129///     Ok(())
130/// }
131/// ```
132pub fn insert<T: SqlCommand + SqlParams, P: for<'a> FromSql<'a> + Send + Sync>(
133    client: &mut Client,
134    entity: T,
135) -> Result<P, Error> {
136    let sql = T::query();
137    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
138        println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
139    }
140
141    let params = entity.params();
142    let row = client.query_one(&sql, &params)?;
143    row.try_get::<_, P>(0)
144}
145
146/// # update
147///
148/// Updates an existing record in the database.
149///
150/// ## Parameters
151/// - `client`: Database connection client
152/// - `entity`: Data object containing the update information (must implement SqlCommand and UpdateParams traits)
153///
154/// ## Return Value
155/// - `Result<u64, Error>`: On success, returns the number of updated records; on failure, returns Error
156///
157/// ## Struct Definition
158/// Structs used with this function should be annotated with the following derive macros:
159///
160/// ```rust,no_run
161/// #[derive(Updateable, UpdateParams)]  // Required macros
162/// #[table("table_name")]               // Table name to update
163/// #[update("field1, field2")]          // Fields to update (optional)
164/// #[where_clause("id = $")]            // Update condition
165/// pub struct MyEntity {
166///     pub id: i32,                     // Fields used in the condition
167///     pub field1: String,              // Fields to be updated
168///     pub field2: i32,                 // Fields to be updated
169///     // ...
170/// }
171/// ```
172///
173/// - `Updateable`: Automatically generates SQL UPDATE statements
174/// - `UpdateParams`: Automatically generates update parameters
175/// - `#[table("table_name")]`: Specifies the table name for the update
176/// - `#[update("field1, field2")]`: Specifies which fields should be updated (if omitted, all fields will be updated)
177/// - `#[where_clause("id = $")]`: Specifies the update condition (`$` will be replaced with parameter value)
178///
179/// ## Example Usage
180/// ```rust,no_run
181/// use postgres::{Client, NoTls, Error};
182/// use parsql::postgres::update;
183///
184/// #[derive(Updateable, UpdateParams)]
185/// #[table("users")]
186/// #[update("name, email")]
187/// #[where_clause("id = $")]
188/// pub struct UpdateUser {
189///     pub id: i32,
190///     pub name: String,
191///     pub email: String,
192///     pub state: i16,  // This field won't be updated as it's not specified in the update attribute
193/// }
194///
195/// fn main() -> Result<(), Error> {
196///     let mut client = Client::connect(
197///         "host=localhost user=postgres dbname=test",
198///         NoTls,
199///     )?;
200///
201///     let update_user = UpdateUser {
202///         id: 1,
203///         name: String::from("John"),
204///         email: String::from("john@example.com"),
205///         state: 2,
206///     };
207///
208///     let update_result = update(&mut client, update_user)?;
209///     println!("Update result: {:?}", update_result);
210///     Ok(())
211/// }
212/// ```
213pub fn update<T: SqlCommand + UpdateParams>(
214    client: &mut postgres::Client,
215    entity: T,
216) -> Result<u64, Error> {
217    let sql = T::query();
218    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
219        println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
220    }
221
222    let params = entity.params();
223    client.execute(&sql, &params)
224}
225
226/// # delete
227///
228/// Deletes a record from the database.
229///
230/// ## Parameters
231/// - `client`: Database connection client
232/// - `entity`: Data object containing the deletion information (must implement SqlCommand and SqlParams traits)
233///
234/// ## Return Value
235/// - `Result<u64, Error>`: On success, returns the number of deleted records; on failure, returns Error
236///
237/// ## Struct Definition
238/// Structs used with this function should be annotated with the following derive macros:
239///
240/// ```rust,no_run
241/// #[derive(Deletable, SqlParams)]   // Required macros
242/// #[table("table_name")]             // Table name to delete from
243/// #[where_clause("id = $")]          // Delete condition
244/// pub struct MyEntity {
245///     pub id: i32,                   // Fields used in the condition
246///     // Other fields can be added, but typically only condition fields are necessary
247/// }
248/// ```
249///
250/// - `Deletable`: Automatically generates SQL DELETE statements
251/// - `SqlParams`: Automatically generates SQL parameters
252/// - `#[table("table_name")]`: Specifies the table name for the deletion
253/// - `#[where_clause("id = $")]`: Specifies the delete condition (`$` will be replaced with parameter value)
254///
255/// ## Example Usage
256/// ```rust,no_run
257/// use postgres::{Client, NoTls, Error};
258/// use parsql::postgres::delete;
259///
260/// #[derive(Deletable, SqlParams)]
261/// #[table("users")]
262/// #[where_clause("id = $")]
263/// pub struct DeleteUser {
264///     pub id: i32,
265/// }
266///
267/// fn main() -> Result<(), Error> {
268///     let mut client = Client::connect(
269///         "host=localhost user=postgres dbname=test",
270///         NoTls,
271///     )?;
272///
273///     let delete_user = DeleteUser { id: 6 };
274///     let delete_result = delete(&mut client, delete_user)?;
275///     
276///     println!("Delete result: {:?}", delete_result);
277///     Ok(())
278/// }
279/// ```
280pub fn delete<T: SqlCommand + SqlParams>(
281    client: &mut postgres::Client,
282    entity: T,
283) -> Result<u64, Error> {
284    let sql = T::query();
285    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
286        println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
287    }
288
289    let params = entity.params();
290    client.execute(&sql, &params)
291}
292
293/// # fetch
294///
295/// Retrieves a single record from the database.
296///
297/// ## Parameters
298/// - `client`: Database connection client
299/// - `params`: Query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
300///
301/// ## Return Value
302/// - `Result<T, Error>`: On success, returns the retrieved record; on failure, returns Error
303///
304/// ## Struct Definition
305/// Structs used with this function should be annotated with the following derive macros:
306///
307/// ```rust,no_run
308/// #[derive(Queryable, FromRow, SqlParams)]  // Required macros
309/// #[table("table_name")]                    // Table name to query
310/// #[where_clause("id = $1")]                // WHERE clause with parameter placeholders
311/// struct GetUser {
312///     id: i32,                              // Parameter for the WHERE clause
313///     name: String,                         // Field to retrieve
314///     email: String,                        // Field to retrieve
315/// }
316/// ```
317pub fn fetch<P, R>(client: &mut Client, params: &P) -> Result<R, Error>
318where
319    P: SqlQuery<R> + SqlParams,
320    R: FromRow,
321{
322    let sql = P::query();
323    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
324        println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
325    }
326
327    let query_params = params.params();
328    let row = client.query_one(&sql, &query_params)?;
329    R::from_row(&row)
330}
331
332/// # fetch_all
333///
334/// Retrieves multiple records from the database.
335///
336/// ## Parameters
337/// - `client`: Database connection client
338/// - `params`: Query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
339///
340/// ## Return Value
341/// - `Result<Vec<T>, Error>`: On success, returns a vector of records; on failure, returns Error
342///
343/// ## Struct Definition
344/// Structs used with this function should be annotated with the following derive macros:
345///
346/// ```rust,no_run
347/// #[derive(Queryable, FromRow, SqlParams)]  // Required macros
348/// #[table("users")]                         // Table name to query
349/// #[where_clause("active = $1")]            // WHERE clause with parameter placeholders
350/// struct GetUsers {
351///     active: bool,                         // Parameter for the WHERE clause
352///     id: i32,                              // Field to retrieve
353///     name: String,                         // Field to retrieve
354///     email: String,                        // Field to retrieve
355/// }
356/// ```
357pub fn fetch_all<P, R>(client: &mut Client, params: &P) -> Result<Vec<R>, Error>
358where
359    P: SqlQuery<R> + SqlParams,
360    R: FromRow,
361{
362    let sql = P::query();
363    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
364        println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
365    }
366
367    let query_params = params.params();
368    let rows = client.query(&sql, &query_params)?;
369
370    let mut results = Vec::with_capacity(rows.len());
371    for row in rows {
372        results.push(R::from_row(&row)?);
373    }
374
375    Ok(results)
376}
377
378/// # get_by_query
379///
380/// Retrieves multiple records from the database using a custom SQL query.
381///
382/// ## Parameters
383/// - `client`: Database connection client
384/// - `query`: Custom SQL query string
385/// - `params`: Array of query parameters
386///
387/// ## Return Value
388/// - `Result<Vec<T>, Error>`: On success, returns the list of found records; on failure, returns Error
389///
390/// ## Example Usage
391/// ```rust,no_run
392/// use postgres::{Client, NoTls, Error};
393/// use parsql::postgres::get_by_query;
394///
395/// #[derive(FromRow, Debug)]
396/// pub struct UserStats {
397///     pub state: i16,
398///     pub user_count: i64,
399/// }
400///
401/// fn main() -> Result<(), Error> {
402///     let mut client = Client::connect(
403///         "host=localhost user=postgres dbname=test",
404///         NoTls,
405///     )?;
406///
407///     let query = "SELECT state, COUNT(*) as user_count FROM users GROUP BY state HAVING COUNT(*) > $1";
408///     let min_count = 5;
409///     
410///     let stats = get_by_query::<UserStats>(&mut client, query, &[&min_count])?;
411///     println!("User stats: {:?}", stats);
412///     Ok(())
413/// }
414/// ```
415pub fn get_by_query<T: FromRow>(
416    client: &mut Client,
417    query: &str,
418    params: &[&(dyn ToSql + Sync)],
419) -> Result<Vec<T>, Error> {
420    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
421        println!("[PARSQL-POSTGRES] Execute SQL: {}", query);
422    }
423
424    let rows = client.query(query, params)?;
425    rows.iter()
426        .map(|row| T::from_row(row))
427        .collect::<Result<Vec<_>, _>>()
428}
429
430/// # select
431///
432/// Retrieves a single record from the database using a custom transformation function.
433/// This is useful when you want to use a custom transformation function instead of the FromRow trait.
434///
435/// ## Parameters
436/// - `client`: Database connection client
437/// - `entity`: Query parameter object (must implement SqlQuery and SqlParams traits)
438/// - `to_model`: Function to convert a Row object to the target object type
439///
440/// ## Return Value
441/// - `Result<T, Error>`: On success, returns the transformed object; on failure, returns Error
442///
443/// ## Struct Definition
444/// Structs used with this function should be annotated with the following derive macros:
445///
446/// ```rust,no_run
447/// #[derive(Queryable, SqlParams)]          // Required macros (FromRow is not needed)
448/// #[table("table_name")]                   // Table name to query
449/// #[where_clause("id = $")]                // Query condition
450/// pub struct MyQueryEntity {
451///     pub id: i32,                         // Field used in the query condition
452///     // Other fields can be added if necessary for the query condition
453/// }
454///
455/// // A separate struct can be used for the return value
456/// pub struct MyResultEntity {
457///     pub id: i32,
458///     pub name: String,
459///     pub count: i64,
460/// }
461/// ```
462///
463/// - `Queryable`: Automatically generates SQL SELECT statements
464/// - `SqlParams`: Automatically generates SQL parameters
465/// - `#[table("table_name")]`: Specifies the table name for the query
466/// - `#[where_clause("id = $")]`: Specifies the query condition (`$` will be replaced with parameter value)
467///
468/// ## Example Usage
469/// ```rust,no_run
470/// use postgres::{Client, NoTls, Error};
471/// use parsql::postgres::select;
472///
473/// #[derive(Queryable, SqlParams)]
474/// #[table("users")]
475/// #[where_clause("id = $")]
476/// pub struct UserQuery {
477///     pub id: i32,
478/// }
479///
480/// impl UserQuery {
481///     pub fn new(id: i32) -> Self {
482///         Self { id }
483///     }
484/// }
485///
486/// // Different return structure
487/// pub struct User {
488///     pub id: i32,
489///     pub name: String,
490/// }
491///
492/// fn main() -> Result<(), Error> {
493///     let mut client = Client::connect(
494///         "host=localhost user=postgres dbname=test",
495///         NoTls,
496///     )?;
497///
498///     // A custom model transformation function is required
499///     let user_query = UserQuery::new(1);
500///     let user = select(&mut client, user_query, |row| {
501///         let id: i32 = row.get("id");
502///         let name: String = row.get("name");
503///         Ok(User { id, name })
504///     })?;
505///     
506///     println!("User: {:?}", user);
507///     Ok(())
508/// }
509/// ```
510pub fn select<T: SqlQuery<T> + SqlParams, F>(
511    client: &mut postgres::Client,
512    entity: T,
513    to_model: F,
514) -> Result<T, Error>
515where
516    F: Fn(&Row) -> Result<T, Error>,
517{
518    let sql = T::query();
519    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
520        println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
521    }
522
523    let params = entity.params();
524
525    match client.query_one(&sql, &params) {
526        Ok(_row) => to_model(&_row),
527        Err(e) => Err(e),
528    }
529}
530
531/// # select_all
532///
533/// Retrieves multiple records from the database using a custom transformation function.
534/// This is useful when you want to use a custom transformation function instead of the FromRow trait.
535///
536/// ## Parameters
537/// - `client`: Database connection client
538/// - `entity`: Query parameter object (must implement SqlQuery and SqlParams traits)
539/// - `to_model`: Function to convert a Row object to the target object type
540///
541/// ## Return Value
542/// - `Result<Vec<T>, Error>`: On success, returns the list of transformed objects; on failure, returns Error
543///
544/// ## Struct Definition
545/// Structs used with this function should be annotated with the following derive macros:
546///
547/// ```rust,no_run
548/// #[derive(Queryable, SqlParams)]          // Required macros (FromRow is not needed)
549/// #[table("table_name")]                   // Table name to query
550/// #[select("id, name, COUNT(*) as count")] // Custom SELECT statement (optional)
551/// #[where_clause("active = $")]            // Query condition
552/// pub struct MyQueryEntity {
553///     pub active: bool,                    // Field used in the query condition
554///     // Other fields can be added if necessary for the query condition
555/// }
556///
557/// // A separate struct can be used for the return value
558/// pub struct MyResultEntity {
559///     pub id: i32,
560///     pub name: String,
561///     pub count: i64,
562/// }
563/// ```
564///
565/// - `Queryable`: Automatically generates SQL SELECT statements
566/// - `SqlParams`: Automatically generates SQL parameters
567/// - `#[table("table_name")]`: Specifies the table name for the query
568/// - `#[select("...")]`: Creates a custom SELECT statement (if omitted, all fields will be selected)
569/// - `#[where_clause("active = $")]`: Specifies the query condition (`$` will be replaced with parameter value)
570///
571/// ## Example Usage
572/// ```rust,no_run
573/// use postgres::{Client, NoTls, Error};
574/// use parsql::postgres::select_all;
575///
576/// #[derive(Queryable, SqlParams)]
577/// #[table("users")]
578/// #[select("id, name, email")]
579/// pub struct UsersQuery {
580///     // Can be empty for a parameterless query
581/// }
582///
583/// impl UsersQuery {
584///     pub fn new() -> Self {
585///         Self {}
586///     }
587/// }
588///
589/// // Different return structure
590/// pub struct User {
591///     pub id: i32,
592///     pub name: String,
593/// }
594///
595/// fn main() -> Result<(), Error> {
596///     let mut client = Client::connect(
597///         "host=localhost user=postgres dbname=test",
598///         NoTls,
599///     )?;
600///
601///     // A custom model transformation function is required
602///     let users_query = UsersQuery::new();
603///     let users = select_all(&mut client, users_query, |row| {
604///         let id: i32 = row.get("id");
605///         let name: String = row.get("name");
606///         User { id, name }
607///     })?;
608///     
609///     println!("Users: {:?}", users);
610///     Ok(())
611/// }
612/// ```
613pub fn select_all<T: SqlQuery<T> + SqlParams, F>(
614    client: &mut postgres::Client,
615    entity: T,
616    to_model: F,
617) -> Result<Vec<T>, Error>
618where
619    F: Fn(&Row) -> Result<T, Error>,
620{
621    let sql = T::query();
622    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
623        println!("[PARSQL-POSTGRES] Execute SQL: {}", sql);
624    }
625
626    let params = entity.params();
627
628    let rows = client.query(&sql, &params)?;
629
630    rows.iter()
631        .map(|row| to_model(row))
632        .collect::<Result<Vec<_>, _>>()
633}
634
635// Geriye dönük uyumluluk için eski get fonksiyonunu koruyalım
636#[deprecated(
637    since = "0.2.0",
638    note = "Renamed to `fetch`. Please use `fetch` function instead."
639)]
640/// # get
641///
642/// Retrieves a single record from the database.
643///
644/// This function is deprecated. Please use `fetch` instead.
645pub fn get<T: SqlQuery<T> + FromRow + SqlParams>(
646    client: &mut Client,
647    params: &T,
648) -> Result<T, Error> {
649    fetch(client, params)
650}
651
652// Geriye dönük uyumluluk için eski get_all fonksiyonunu koruyalım
653#[deprecated(
654    since = "0.2.0",
655    note = "Renamed to `fetch_all`. Please use `fetch_all` function instead."
656)]
657/// # get_all
658///
659/// Retrieves multiple records from the database.
660///
661/// This function is deprecated. Please use `fetch_all` instead.
662pub fn get_all<T: SqlQuery<T> + FromRow + SqlParams>(
663    client: &mut Client,
664    params: &T,
665) -> Result<Vec<T>, Error> {
666    fetch_all(client, params)
667}