safety_postgres/access/
postgres_base.rs

1use tokio;
2use tokio_postgres::{NoTls, Error as PGError, row::Row, Client, Statement};
3use tokio_postgres::types::ToSql;
4use crate::access::app_config::AppConfig;
5use crate::access::conditions::Conditions;
6use crate::access::errors::PostgresBaseError;
7use crate::access::generate_params::{box_param_generator, params_ref_generator};
8use crate::access::join_tables::JoinTables;
9use crate::access::sql_base::{InsertRecords, QueryColumns, SqlType, UpdateSets};
10use crate::access::validators::validate_alphanumeric_name;
11
12/// Represents a connection config to a PostgreSQL database.
13///
14/// # Example
15/// ```rust
16/// use safety_postgres::access::postgres_base::PostgresBase;
17/// use safety_postgres::access::sql_base::QueryColumns;
18///
19/// async fn postgres_query() {
20///     let mut postgres = PostgresBase::new("table_name")
21///         .expect("postgres base init failed");
22///     postgres.connect().await.expect("connect failed");
23///
24///     let query_columns = QueryColumns::new(true);
25///
26///     postgres.query_raw(&query_columns).await.expect("query failed");
27/// }
28/// ```
29pub struct PostgresBase {
30    username: String,
31    password: String,
32    hostname: String,
33    port: u32,
34    dbname: String,
35    table_name: String,
36    schema_name: String,
37    client: Option<Client>
38}
39
40/// Represents the type of execution.
41///
42/// This enum is used to determine the type of SQL execution to be performed.
43/// It can be either `Execute` or `Query`.
44enum ExecuteType {
45    Execute,
46    Query,
47}
48
49/// Represents the result of an execution.
50///
51/// This enum is used to represent the result of an SQL execution operation.
52///
53/// # Variants
54///
55/// - `Execute(u64)`: Represents the result of an execution operation that returns a single value of type `u64`.
56///
57/// - `Query(Vec<Row>)`: Represents the result of a query operation that returns multiple rows of type `Vec<Row>`.
58enum ExecuteResult {
59    Execute(u64),
60    Query(Vec<Row>),
61}
62
63impl PostgresBase {
64    /// Creates a new instance of `PostgresBase` for interacting with a PostgreSQL database.
65    ///
66    /// # Arguments
67    ///
68    /// * `table_name` - The name of the table to interact with.
69    ///
70    /// # Returns
71    ///
72    /// Returns a `Result` containing the new `PostgresBase` instance if successful,
73    /// or a `PostgresBaseError` if an error occurs.
74    ///
75    /// # Example
76    /// ```rust
77    /// use safety_postgres::access::postgres_base::PostgresBase;
78    /// # std::env::set_var("DB_USER", "username");
79    /// # std::env::set_var("DB_PASSWORD", "password");
80    /// # std::env::set_var("DB_HOST", "localhost");
81    ///
82    /// let mut postgres = PostgresBase::new("table_name").expect("PostgresBase init failed");
83    /// ```
84    pub fn new(table_name: &str) -> Result<Self, PostgresBaseError> {
85        let valid_table_name;
86        if !validate_alphanumeric_name(table_name, "_") {
87            return Err(PostgresBaseError::InputInvalidError(format!("{} is invalid name. Please confirm the rule of the 'table_name'", table_name)));
88        }
89        else {
90            valid_table_name = table_name;
91        }
92
93        let config = match AppConfig::new() {
94            Ok(config) => config,
95            Err(e) => return Err(PostgresBaseError::ConfigNotDefinedError(e)),
96        };
97        let schema_name: String;
98        let table_name_w_schema = match std::env::var("DB_SCHEMA") {
99            Ok(schema) => {
100
101                if !validate_alphanumeric_name(&schema, "_") {
102                    eprintln!("{} is invalid schema name. The schema is ignored so if you need to add schema please use 'set_schema' method.", schema);
103                    schema_name = "".to_string();
104                    valid_table_name.to_string()
105                } else {
106                    schema_name = schema.clone();
107                    format!("{}.{}", schema, valid_table_name)
108                }
109            },
110            Err(_) => {
111                schema_name = "".to_string();
112                valid_table_name.to_string()
113            }
114        };
115
116        let (username, password, hostname, port, dbname) = config.get_values();
117
118        Ok(PostgresBase {
119            username: username.to_string(),
120            password: password.to_string(),
121            hostname: hostname.to_string(),
122            port,
123            dbname: dbname.to_string(),
124            table_name: table_name_w_schema,
125            schema_name,
126            client: None,
127        })
128    }
129
130    /// Connects to a PostgreSQL database using the provided configuration.
131    ///
132    /// # Returns
133    ///
134    /// Returns a result indicating whether the connection was successful or an error occurred.
135    /// If the connection is successful, `Ok(())` is returned.
136    /// If an error occurs, `Err(PGError)` is returned.
137    ///
138    /// # Example
139    ///
140    /// ```rust
141    /// use safety_postgres::access::postgres_base::PostgresBase;
142    ///
143    /// async fn postgres_connect() {
144    ///     let mut postgres = PostgresBase::new("your_table_name").expect("PostgresBase struct return error");
145    ///     let _ = postgres.connect().await.expect("connect failed");
146    /// }
147    /// ```
148    pub async fn connect(&mut self) -> Result<(), PGError> {
149        let (client, connection) = tokio_postgres::Config::new()
150            .user(self.username.as_str())
151            .password(self.password.as_str())
152            .host(self.hostname.as_str())
153            .port(self.port as u16)
154            .dbname(self.dbname.as_str())
155            .connect(NoTls).await?;
156
157        tokio::spawn(async move {
158            if let Err(e) = connection.await {
159                eprintln!("connection error: {}", e);
160            }
161        });
162
163        self.client = Some(client);
164        Ok(())
165    }
166
167    /// Executes a raw query on the database and returns the result.
168    ///
169    /// # Arguments
170    ///
171    /// * `query_columns` - A `QueryColumns` struct reference specifying the columns to query.
172    ///
173    /// # Returns
174    ///
175    /// * `Ok(Vec<Row>)` - Get the values if the query was successful.
176    /// * `Err(PostgresBaseError)` - If an error occurred during the query process.
177    ///
178    /// # Errors
179    ///
180    /// Returns a `PostgresBaseError` if there was an error executing the query.
181    pub async fn query_raw(&self, query_columns: &QueryColumns) -> Result<Vec<Row>, PostgresBaseError> {
182        let empty_join_table = JoinTables::new();
183        let empty_condition = Conditions::new();
184        self.query_inner_join_conditions(query_columns, &empty_join_table, &empty_condition).await
185    }
186
187    /// Queries the database for data based on the provided query column and conditions.
188    ///
189    /// # Arguments
190    ///
191    /// * `query_column` - The columns using reference of the `QueryColumns` struct to query.
192    /// * `conditions` - The conditions using reference of the `Conditions` to apply to the query.
193    ///
194    /// # Returns
195    ///
196    /// * `Ok(Vec<Row>)` - Get the values if the query was successful.
197    /// * `Err(PostgresBaseError)` - If an error occurred during the query process.
198    ///
199    /// # Errors
200    ///
201    /// Returns a `PostgresBaseError` if there was an error querying the database.
202    pub async fn query_condition_raw(&self, query_column: &QueryColumns, conditions: &Conditions) -> Result<Vec<Row>, PostgresBaseError> {
203        let join_tables = JoinTables::new();
204        self.query_inner_join_conditions(query_column, &join_tables, conditions).await
205    }
206
207    /// Queries the database with inner join and conditions.
208    ///
209    /// # Arguments
210    ///
211    /// * `query_columns` - The columns using reference of the `QueryColumns` struct to query.
212    /// * `join_tables` - The tables collection using reference of the `JoinTables` to join.
213    /// * `conditions` - The conditions using reference of the `Conditions` to apply to the query.
214    ///
215    /// # Returns
216    ///
217    /// * `Ok(Vec<Row>)` - Get the values if the query was successful
218    /// * `Err(PostgresBaseError)` - If an error occurred during the query process.
219    ///
220    /// # Examples
221    ///
222    /// ```rust
223    /// use safety_postgres::access::conditions::Conditions;
224    /// use safety_postgres::access::join_tables::JoinTables;
225    /// use safety_postgres::access::postgres_base::PostgresBase;
226    /// use safety_postgres::access::sql_base::QueryColumns;
227    ///
228    /// async fn postgres_query() {
229    ///     let mut db = PostgresBase::new("table_name").unwrap();
230    ///     db.connect().await.expect("connection failed");
231    ///
232    ///     let query_column = QueryColumns::new(true);
233    ///     let join_tables = JoinTables::new();
234    ///     let conditions = Conditions::new();
235    ///
236    ///     /**
237    ///     * Your code....
238    ///     */
239    ///
240    ///     let result = db.query_condition_raw(&query_column, &conditions).await;
241    ///     match result {
242    ///         Ok(rows) => {
243    ///             for row in rows {
244    ///                 // Do something with the row
245    ///             }
246    ///         }
247    ///         Err(error) => {
248    ///             // Handle the error
249    ///         }
250    ///     }
251    /// }
252    /// ```
253    pub async fn query_inner_join_conditions(&self, query_columns: &QueryColumns, join_tables: &JoinTables, conditions: &Conditions) -> Result<Vec<Row>, PostgresBaseError> {
254        let query_statement: String = SqlType::Select(query_columns).sql_build(self.table_name.as_str());
255        let mut statement_vec: Vec<String> = vec![query_statement];
256
257        if !join_tables.is_tables_empty() {
258            let join_statement = join_tables.generate_statement_text(self.table_name.as_str());
259            statement_vec.push(join_statement);
260        }
261
262        let params_values = conditions.get_flat_values();
263        if !conditions.is_empty() {
264            let condition_statement = conditions.generate_statement_text(0);
265            statement_vec.push(condition_statement);
266        }
267
268        let statement = statement_vec.join(" ");
269        let res = self.query(&statement, &params_values).await?;
270        Ok(res)
271    }
272
273    /// Inserts records into the database table.
274    ///
275    /// # Arguments
276    ///
277    /// * `insert_records` - An `InsertRecords` object reference containing the records to be inserted.
278    ///
279    /// # Returns
280    ///
281    /// * `Ok(())` - If the records were inserted successfully.
282    /// * `Err(PostgresBaseError)` - If an error occurred during the insertion process.
283    ///
284    /// # Examples
285    ///
286    /// ```
287    /// use safety_postgres::access::postgres_base::PostgresBase;
288    /// use safety_postgres::access::sql_base::InsertRecords;
289    ///
290    /// async fn postgres_insert() {
291    ///     let mut db = PostgresBase::new("my_table").expect("db struct init failed");
292    ///     db.connect().await.expect("connection failed");
293    ///     let mut insert_records = InsertRecords::new(&["column1", "column2"]);
294    ///     insert_records.add_record(&["value1", "value2"]).expect("add record failed");
295    ///
296    ///     let result = db.insert(&insert_records).await.expect("insert failed");
297    /// }
298    /// ```
299    pub async fn insert(&self, insert_records: &InsertRecords) -> Result<(), PostgresBaseError> {
300        let params_values = insert_records.get_flat_values();
301        let insert = SqlType::Insert(insert_records);
302        let statement = insert.sql_build(self.table_name.as_str());
303        let res = self.execute(&statement, &params_values).await?;
304        println!("{} record(s) are inserted.", res);
305        Ok(())
306    }
307
308    /// Updates records in the specified table based on the given update sets.
309    ///
310    /// # Arguments
311    ///
312    /// - `update_set`: An `UpdateSets` object reference which containing the fields to update.
313    /// - `allow_all_update`: A boolean flag indicating whether updating all records is allowed.
314    ///
315    /// # Returns
316    ///
317    /// - `Ok(())` if the update is successful.
318    /// - `Err(PostgresBaseError)` if an error occurs during the update.
319    pub async fn update(&self, update_set: &UpdateSets, allow_all_update: bool) -> Result<(), PostgresBaseError> {
320        if allow_all_update {
321            let condition = Conditions::new();
322            self.update_condition(update_set, &condition).await
323        }
324        else {
325            Err(PostgresBaseError::UnsafeExecutionError("'update' method will update all records in the specified table so please consider to use 'update_condition' instead of this.".to_string()))
326        }
327    }
328
329    /// Updates records in the table based on the specified update set and conditions.
330    ///
331    /// # Arguments
332    ///
333    /// * `update_set` - The `UpdateSets` reference specifying the columns and values to update.
334    /// * `conditions` - The `Conditions` reference specifying the records to update.
335    ///
336    /// # Returns
337    ///
338    /// * `Ok(())` - If the update operation is successful.
339    /// * `Err(PostgresBaseError)` - If an error occurs during the update operation.
340    ///
341    /// # Example
342    ///
343    /// ```rust
344    /// use safety_postgres::access::conditions::{Conditions, IsInJoinedTable};
345    /// use safety_postgres::access::postgres_base::PostgresBase;
346    /// use safety_postgres::access::sql_base::UpdateSets;
347    ///
348    /// async fn postgres_update() {
349    ///     let mut database = PostgresBase::new("my_table").expect("postgres base init failed");
350    ///     database.connect().await.expect("connection failed");
351    ///
352    ///     let mut update_set = UpdateSets::new();
353    ///     update_set.add_set("column1", "value1").unwrap();
354    ///
355    ///     let mut conditions = Conditions::new();
356    ///     conditions.add_condition_from_str(
357    ///         "column1",
358    ///         "value1",
359    ///         "eq",
360    ///         "",
361    ///         IsInJoinedTable::No)
362    ///         .expect("adding condition failed");
363    ///
364    ///     database.update_condition(&update_set, &conditions).await.expect("update failed");
365    /// }
366    /// ```
367    pub async fn update_condition(&self, update_set: &UpdateSets, conditions: &Conditions) -> Result<(), PostgresBaseError> {
368        let set_num = update_set.get_num_values();
369        let mut params_values = update_set.get_flat_values();
370        let statement_base = SqlType::Update(update_set).sql_build(self.table_name.as_str());
371        let mut statement_vec = vec![statement_base];
372
373        params_values.extend(conditions.get_flat_values());
374        if !conditions.is_empty() {
375            let statement_condition = conditions.generate_statement_text(set_num);
376            statement_vec.push(statement_condition);
377        }
378        let statement = statement_vec.join(" ");
379
380        let res = self.execute(&statement, &params_values).await?;
381        println!("{} record(s) are updated.", res);
382        Ok(())
383    }
384
385    /// Delete records from the database table based on given conditions.
386    ///
387    /// # Arguments
388    ///
389    /// * `conditions` - The reference of the conditions used to filter the records to be deleted.
390    ///
391    /// # Returns
392    ///
393    /// * `Ok(())` - Returns this if the deletion is successful
394    /// * `PostgresBaseError` - Returns an error of type `PostgresBaseError` when deletion process failed.
395    ///
396    /// # Examples
397    ///
398    /// ```
399    /// use safety_postgres::access::conditions::ComparisonOperator::Grater;
400    /// use safety_postgres::access::conditions::{Conditions, IsInJoinedTable};
401    /// use safety_postgres::access::conditions::LogicalOperator::FirstCondition;
402    /// use safety_postgres::access::postgres_base::PostgresBase;
403    ///
404    /// async fn postgres_delete() {
405    ///     let mut database = PostgresBase::new("my_table").expect("db init failed");
406    ///     database.connect().await.expect("connecting failed");
407    ///
408    ///     let mut conditions = Conditions::new();
409    ///     conditions.add_condition(
410    ///         "column1",
411    ///         "value1",
412    ///         Grater,
413    ///         FirstCondition,
414    ///         IsInJoinedTable::No).expect("adding condition failed");
415    ///
416    ///     database.delete(&conditions).await.expect("delete failed");
417    /// }
418    /// ```
419    pub async fn delete(&self, conditions: &Conditions) -> Result<(), PostgresBaseError> {
420        if conditions.is_empty() {
421            return Err(PostgresBaseError::UnsafeExecutionError("'delete' method unsupports deleting records without any condition.".to_string()))
422        }
423
424        let statement_base = SqlType::Delete.sql_build(self.table_name.as_str());
425        let mut  statement_vec = vec![statement_base];
426        let params_values = conditions.get_flat_values();
427        statement_vec.push(conditions.generate_statement_text(0));
428
429        let statement = statement_vec.join(" ");
430        let res = self.execute(&statement, &params_values).await?;
431        println!("{} record(s) are deleted.", res);
432
433        Ok(())
434    }
435
436    /// Sets the name of the database.
437    ///
438    /// This method validates the given `dbname` parameter to ensure it consists only of alphanumeric characters and underscores.
439    /// If the validation fails, an error message is printed to the standard error output and the change is rejected.
440    ///
441    /// # Arguments
442    ///
443    /// * `dbname` - The new name of the database.
444    ///
445    /// # Returns
446    ///
447    /// The updated `self` object.
448    pub fn set_dbname(&mut self, dbname: &str) -> &mut Self {
449        if !validate_alphanumeric_name(dbname, "_") {
450            eprintln!("Unexpected dbname inputted so the change is rejected.");
451            return self;
452        }
453        self.dbname = dbname.to_string();
454        self
455    }
456
457    /// Sets the schema for the database table.
458    ///
459    /// # Arguments
460    ///
461    /// * `schema_name` - The new name of the schema to set.
462    ///
463    /// # Returns
464    ///
465    /// The modified `Self` object.
466    pub fn set_schema(&mut self, schema_name: &str) -> &mut Self {
467        if !validate_alphanumeric_name(schema_name, "_") {
468            eprintln!("Unexpected dbname inputted so the change is rejected.");
469            return self;
470        }
471
472        let table_name: String;
473        if self.table_name.contains(".") {
474            let origin_table_param = self.table_name.split(".").collect::<Vec<&str>>();
475            table_name = origin_table_param[1].to_string();
476        }
477        else {
478            table_name = self.table_name.clone();
479        }
480
481        if schema_name.is_empty() {
482            self.table_name = table_name;
483        }
484        else {
485            self.table_name = format!("{}.{}", schema_name, table_name);
486        }
487        self.schema_name = schema_name.to_string();
488        self
489    }
490
491    /// Sets the port for the postgresql.
492    ///
493    /// # Arguments
494    ///
495    /// * `port` - The new port setting for the postgresql
496    ///
497    /// # Returns
498    ///
499    /// The modified `self` object.
500    pub fn set_port(&mut self, port: u32) -> &mut Self {
501        self.port = port;
502        self
503    }
504
505    /// Get the configuration string for connecting to the PostgreSQL database.
506    ///
507    /// # Returns
508    ///
509    /// * Configuration - Representing the configuration for connecting to the database.
510    pub fn get_config(&self) -> String {
511        let mut schema_name: Option<&str> = None;
512
513        if self.table_name.contains(".") {
514            let schema_table: Vec<&str> = self.table_name.split(".").collect();
515            schema_name = Some(schema_table[0]);
516        }
517
518        if let Some(schema) = schema_name {
519            format!("postgresql://{}:{}@{}:{}/{}?options=--search_path={}", self.username, self.password, self.hostname, self.port, self.dbname, schema)
520        } else {
521            format!("postgresql://{}:{}@{}:{}/{}", self.username, self.password, self.hostname, self.port, self.dbname)
522        }
523    }
524
525    /// Executes a query statement with the given parameters and returns a vector of rows as the result.
526    ///
527    /// # Arguments
528    ///
529    /// - `statement_str`: A reference to a `String` containing the query statement to be executed.
530    /// - `params`: A slice of `String` containing the parameters to be used in the query.
531    ///
532    /// # Returns
533    ///
534    /// * `Ok(Vec<Row>)` - Returns vector of `Row` if the query was executed successfully.
535    /// * `Err(PostgresBaseError)` - Returns `PostgresBaseError` if an error occurred during this process.
536    ///
537    /// # Errors
538    ///
539    /// This function can return a `PostgresBaseError` in the following cases:
540    ///
541    /// - If an internal execution error occurs, an `UnexpectedError` variant of `PostgresBaseError` will be returned.
542    async fn query(&self, statement_str: &String, params: &[String]) -> Result<Vec<Row>, PostgresBaseError> {
543        let result = self.execute_core(statement_str, params, ExecuteType::Query).await?;
544        match result {
545            ExecuteResult::Query(res) => Ok(res),
546            _ => return Err(PostgresBaseError::UnexpectedError("Execution internal error occurred, please contact the developer.".to_string())),        }
547    }
548
549    /// Executes a database statement with parameters asynchronously.
550    ///
551    /// # Arguments
552    ///
553    /// * `statement_str` - The database statement to execute.
554    /// * `params` - The parameters to bind to the statement.
555    ///
556    /// # Returns
557    ///
558    /// * `Ok(u64)` - Returns the number of rows affected by the statement if successful
559    /// * `Err(PostgresBaseError)` - Returns an error if an unexpected error occurred
560    ///
561    /// # Errors
562    ///
563    /// Returns an `PostgresBaseError` if an unexpected error occurred while executing the statement.
564    async fn execute(&self, statement_str: &String, params: &[String]) -> Result<u64, PostgresBaseError> {
565        let result = self.execute_core(statement_str, params, ExecuteType::Execute).await?;
566        match result {
567            ExecuteResult::Execute(res) => Ok(res),
568            _ => return Err(PostgresBaseError::UnexpectedError("Execution internal error occurred, please contact the developer.".to_string())),
569        }
570    }
571
572    /// Executes a PostgreSQL statement with the given parameters and return the result.
573    ///
574    /// # Arguments
575    ///
576    /// * `statement_str` - The statement string to execute.
577    /// * `params` - The parameters to bind to the statement.
578    /// * `execute_type` - The type of execution (Execute or Query).
579    ///
580    /// # Returns
581    ///
582    /// * Ok(ExecuteResult) - Returns result valiant containing the execution result
583    /// * Err(PostgresBaseError) - Returns an error if the execution failed
584    async fn execute_core(&self, statement_str: &String, params: &[String], execute_type: ExecuteType) -> Result<ExecuteResult, PostgresBaseError> {
585        let client = match self.client.as_ref() {
586            Some(client) => client,
587            None => return Err(PostgresBaseError::ConnectionNotFoundError("Client does not exist. Please connect the PostgreSQL first via connect method.".to_string())),
588        };
589
590        let box_params = box_param_generator(params);
591        let params_ref: Vec<&(dyn ToSql + Sync)> = params_ref_generator(&box_params);
592
593        let statement: Statement = match client.prepare(statement_str).await {
594            Ok(statement) => statement,
595            Err(e) => return Err(PostgresBaseError::TokioPostgresError(format!("Prepare statement generation failed in tokio-postgres like {}", e))),
596        };
597
598        match execute_type {
599            ExecuteType::Execute => {
600                match client.execute(&statement, &params_ref).await {
601                    Ok(res) => Ok(ExecuteResult::Execute(res)),
602                    Err(e) => return Err(PostgresBaseError::SQLExecutionError(format!("SQL executor failed due to {}", e))),
603                }
604            }
605            ExecuteType::Query => {
606                match client.query(&statement, &params_ref).await {
607                    Ok(res) => Ok(ExecuteResult::Query(res)),
608                    Err(e) => return Err(PostgresBaseError::SQLExecutionError(format!("SQL executor failed due to {}", e))),
609                }
610            }
611        }
612    }
613}
614
615#[cfg(test)]
616mod tests {
617    use crate::access::errors::PostgresBaseError;
618    use crate::access::postgres_base::PostgresBase;
619
620    #[test]
621    fn test_set_and_get_connect_conf() {
622        std::env::set_var("DB_USER", "username");
623        std::env::set_var("DB_PASSWORD", "password");
624        std::env::set_var("DB_HOST", "localhost");
625
626        let mut postgres = PostgresBase::new("test_table").unwrap();
627
628        let config = postgres.get_config();
629
630        assert_eq!(config, "postgresql://username:password@localhost:5432/postgres");
631
632        postgres.set_dbname("test");
633        let config = postgres.get_config();
634
635        assert_eq!(config, "postgresql://username:password@localhost:5432/test");
636
637        postgres.set_port(12345);
638        let config = postgres.get_config();
639
640        assert_eq!(config, "postgresql://username:password@localhost:12345/test");
641
642        postgres.set_schema("schema");
643        let config = postgres.get_config();
644
645        assert_eq!(config, "postgresql://username:password@localhost:12345/test?options=--search_path=schema");
646    }
647
648    #[test]
649    fn test_new_get_invalid_value() {
650        std::env::set_var("DB_USER", "username");
651        std::env::set_var("DB_PASSWORD", "password");
652        std::env::set_var("DB_HOST", "localhost");
653
654        let Err(e) = PostgresBase::new("tab;le") else { panic!() };
655        assert_eq!(e, PostgresBaseError::InputInvalidError(format!("{} is invalid name. Please confirm the rule of the 'table_name'", "tab;le")));
656    }
657
658    #[test]
659    fn test_set_invalid_value() {
660        std::env::set_var("DB_USER", "username");
661        std::env::set_var("DB_PASSWORD", "password");
662        std::env::set_var("DB_HOST", "localhost");
663
664        let mut postgres = PostgresBase::new("table").unwrap();
665
666        postgres.set_dbname("db;Name");
667        let config = postgres.get_config();
668
669        assert_eq!(config, "postgresql://username:password@localhost:5432/postgres");
670
671        postgres.set_schema("sch;ma");
672        let config = postgres.get_config();
673
674        assert_eq!(config, "postgresql://username:password@localhost:5432/postgres");
675    }
676}