Some checks failed
Rhai Tests / Run Rhai Tests (pull_request) Has been cancelled
- Pull the PostgreSQL image before installation to ensure the latest version is used. This improves reliability and reduces the chance of using outdated images. Improves the robustness of the installation process. - Added comprehensive unit tests for `PostgresInstallerConfig`, `PostgresInstallerError`, `install_postgres`, `create_database`, `execute_sql`, and `is_postgres_running` functions to ensure correctness and handle potential errors effectively. Improves code quality and reduces the risk of regressions. |
||
---|---|---|
.. | ||
installer.rs | ||
mod.rs | ||
postgresclient.rs | ||
README.md | ||
tests.rs |
PostgreSQL Client Module
The PostgreSQL client module provides a simple and efficient way to interact with PostgreSQL databases in Rust. It offers connection management, query execution, and a builder pattern for flexible configuration.
Features
- Connection Management: Automatic connection handling and reconnection
- Query Execution: Simple API for executing queries and fetching results
- Builder Pattern: Flexible configuration with authentication support
- Environment Variable Support: Easy configuration through environment variables
- Thread Safety: Safe to use in multi-threaded applications
Usage
Basic Usage
use sal::postgresclient::{execute, query, query_one};
// Execute a query
let create_table_query = "CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT)";
execute(create_table_query, &[]).expect("Failed to create table");
// Insert data
let insert_query = "INSERT INTO users (name) VALUES ($1) RETURNING id";
let rows = query(insert_query, &[&"John Doe"]).expect("Failed to insert data");
let id: i32 = rows[0].get(0);
// Query data
let select_query = "SELECT id, name FROM users WHERE id = $1";
let row = query_one(select_query, &[&id]).expect("Failed to query data");
let name: String = row.get(1);
println!("User: {} (ID: {})", name, id);
Connection Management
The module manages connections automatically, but you can also reset the connection if needed:
use sal::postgresclient::reset;
// Reset the PostgreSQL client connection
reset().expect("Failed to reset connection");
Builder Pattern
The module provides a builder pattern for flexible configuration:
use sal::postgresclient::{PostgresConfigBuilder, with_config};
// Create a configuration builder
let config = PostgresConfigBuilder::new()
.host("db.example.com")
.port(5432)
.user("postgres")
.password("secret")
.database("mydb")
.application_name("my-app")
.connect_timeout(30)
.ssl_mode("require");
// Connect with the configuration
let client = with_config(config).expect("Failed to connect");
Configuration
Environment Variables
The module uses the following environment variables for configuration:
POSTGRES_HOST
: PostgreSQL server host (default: localhost)POSTGRES_PORT
: PostgreSQL server port (default: 5432)POSTGRES_USER
: PostgreSQL username (default: postgres)POSTGRES_PASSWORD
: PostgreSQL passwordPOSTGRES_DB
: PostgreSQL database name (default: postgres)
Connection String
The connection string is built from the configuration options:
host=localhost port=5432 user=postgres dbname=postgres
With authentication:
host=localhost port=5432 user=postgres password=secret dbname=postgres
With additional options:
host=localhost port=5432 user=postgres dbname=postgres application_name=my-app connect_timeout=30 sslmode=require
API Reference
Connection Functions
get_postgres_client() -> Result<Arc<PostgresClientWrapper>, PostgresError>
: Get the PostgreSQL client instancereset() -> Result<(), PostgresError>
: Reset the PostgreSQL client connection
Query Functions
execute(query: &str, params: &[&(dyn postgres::types::ToSql + Sync)]) -> Result<u64, PostgresError>
: Execute a query and return the number of affected rowsquery(query: &str, params: &[&(dyn postgres::types::ToSql + Sync)]) -> Result<Vec<Row>, PostgresError>
: Execute a query and return the results as a vector of rowsquery_one(query: &str, params: &[&(dyn postgres::types::ToSql + Sync)]) -> Result<Row, PostgresError>
: Execute a query and return a single rowquery_opt(query: &str, params: &[&(dyn postgres::types::ToSql + Sync)]) -> Result<Option<Row>, PostgresError>
: Execute a query and return an optional row
Configuration Functions
PostgresConfigBuilder::new() -> PostgresConfigBuilder
: Create a new PostgreSQL configuration builderwith_config(config: PostgresConfigBuilder) -> Result<Client, PostgresError>
: Create a new PostgreSQL client with custom configuration
Error Handling
The module uses the postgres::Error
type for error handling:
use sal::postgresclient::{query, query_one};
// Handle errors
match query("SELECT * FROM users", &[]) {
Ok(rows) => {
println!("Found {} users", rows.len());
},
Err(e) => {
eprintln!("Error querying users: {}", e);
}
}
// Using query_one with no results
match query_one("SELECT * FROM users WHERE id = $1", &[&999]) {
Ok(_) => {
println!("User found");
},
Err(e) => {
eprintln!("User not found: {}", e);
}
}
Thread Safety
The PostgreSQL client module is designed to be thread-safe. It uses Arc
and Mutex
to ensure safe concurrent access to the client instance.
Examples
Basic CRUD Operations
use sal::postgresclient::{execute, query, query_one};
// Create
let create_query = "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id";
let rows = query(create_query, &[&"Alice", &"alice@example.com"]).expect("Failed to create user");
let id: i32 = rows[0].get(0);
// Read
let read_query = "SELECT id, name, email FROM users WHERE id = $1";
let row = query_one(read_query, &[&id]).expect("Failed to read user");
let name: String = row.get(1);
let email: String = row.get(2);
// Update
let update_query = "UPDATE users SET email = $1 WHERE id = $2";
let affected = execute(update_query, &[&"new.alice@example.com", &id]).expect("Failed to update user");
// Delete
let delete_query = "DELETE FROM users WHERE id = $1";
let affected = execute(delete_query, &[&id]).expect("Failed to delete user");
Transactions
Transactions are not directly supported by the module, but you can use the PostgreSQL client to implement them:
use sal::postgresclient::{execute, query};
// Start a transaction
execute("BEGIN", &[]).expect("Failed to start transaction");
// Perform operations
let insert_query = "INSERT INTO accounts (user_id, balance) VALUES ($1, $2)";
execute(insert_query, &[&1, &1000.0]).expect("Failed to insert account");
let update_query = "UPDATE users SET has_account = TRUE WHERE id = $1";
execute(update_query, &[&1]).expect("Failed to update user");
// Commit the transaction
execute("COMMIT", &[]).expect("Failed to commit transaction");
// Or rollback in case of an error
// execute("ROLLBACK", &[]).expect("Failed to rollback transaction");
Testing
The module includes comprehensive tests for both unit and integration testing:
// Unit tests
#[test]
fn test_postgres_config_builder() {
let config = PostgresConfigBuilder::new()
.host("test-host")
.port(5433)
.user("test-user");
let conn_string = config.build_connection_string();
assert!(conn_string.contains("host=test-host"));
assert!(conn_string.contains("port=5433"));
assert!(conn_string.contains("user=test-user"));
}
// Integration tests
#[test]
fn test_basic_postgres_operations() {
// Skip if PostgreSQL is not available
if !is_postgres_available() {
return;
}
// Create a test table
let create_table_query = "CREATE TEMPORARY TABLE test_table (id SERIAL PRIMARY KEY, name TEXT)";
execute(create_table_query, &[]).expect("Failed to create table");
// Insert data
let insert_query = "INSERT INTO test_table (name) VALUES ($1) RETURNING id";
let rows = query(insert_query, &[&"test"]).expect("Failed to insert data");
let id: i32 = rows[0].get(0);
// Query data
let select_query = "SELECT name FROM test_table WHERE id = $1";
let row = query_one(select_query, &[&id]).expect("Failed to query data");
let name: String = row.get(0);
assert_eq!(name, "test");
}