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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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}