PostgreSQL Embedded

repository·main·Indexed 18 days ago

https://github.com/theseus-rs/postgresql-embedded

A Rust library that allows developers to install and run a PostgreSQL database locally on Linux, MacOS, or Windows. It manages the downloading, installation, and lifecycle of a PostgreSQL process, providing an experience similar to SQLite. The project includes crates for downloading archives (postgresql_archive), executing command line utilities (postgresql_commands), managing extensions (postgresql_extensions), and bundling the database directly into application binaries.

Tokens
19.5K
Snippets
58
Records
75
Agent score
63%

What's inside postgresql-embedded

  1. Information: PostgreSQL Binary Caching and Downloads

    main

    PostgreSQL binaries are downloaded either at build time (if bundled is enabled) or at runtime.

    Rate Limiting: You can set the GITHUB_TOKEN environment variable to a GitHub personal access token to increase download rate limits.

    Cache Locations:

    • Unix: $HOME/.theseus/postgresql
    • Windows: %USERPROFILE%\.theseus\postgresql

    Performance Tip: Specifying an exact version (e.g., =16.4.0) via POSTGRESQL_VERSION or in Settings improves performance by avoiding version match queries after the initial download.

  2. Bundle PostgreSQL into your binary

    main

    To include the PostgreSQL installation archive directly within your application executable, enable the bundled feature.

    1. At Compile Time: Set the POSTGRESQL_VERSION environment variable to a specific version (e.g., =17.2.0) to determine which archive to download and include.
    2. At Runtime: You must provide an explicit matching version in your Settings object using VersionReq to ensure the runtime uses the bundled version.

    If POSTGRESQL_VERSION is not set during build, postgresql_archive::LATEST is used.

    To use a custom GitHub source for releases, set the POSTGRESQL_RELEASES_URL environment variable. The repository must follow the structure used by theseus-rs/postgresql_binaries.

    use postgresql_embedded::{Result, Settings, VersionReq};
    use std::str::FromStr;
    
    #[tokio::main]
    async fn main() -> Result<()> {
        let settings = Settings {
            version: VersionReq::from_str("=17.2.0")?,
            ..Default::default()
        };
        // Use settings with PostgreSQL::new(settings)
        Ok(())
    }
  3. Use the Synchronous API with PostgreSQL Embedded

    main

    If you prefer a blocking API, use postgresql_embedded::blocking::PostgreSQL. This provides a synchronous interface for setup, starting, and managing the database instance.

    Note: This requires the blocking feature to be enabled.

    use postgresql_embedded::Result;
    use postgresql_embedded::blocking::PostgreSQL;
    
    fn main() -> Result<()> {
        let mut postgresql = PostgreSQL::default();
        postgresql.setup()?;
        postgresql.start()?;
    
        let database_name = "test";
        postgresql.create_database(database_name)?;
        postgresql.database_exists(database_name)?;
        postgresql.drop_database(database_name)?;
    
        postgresql.stop()
    }
  4. Use the Asynchronous API with PostgreSQL Embedded

    main

    The asynchronous API allows you to manage a PostgreSQL instance using tokio. You can initialize a PostgreSQL instance with default settings, set it up, start the process, and perform database operations like creating or dropping databases.

    Note: This requires the tokio feature to be enabled.

    use postgresql_embedded::{PostgreSQL, Result};
    
    #[tokio::main]
    async fn main() -> Result<()> {
        let mut postgresql = PostgreSQL::default();
        postgresql.setup().await?;
        postgresql.start().await?;
    
        let database_name = "test";
        postgresql.create_database(database_name).await?;
        postgresql.database_exists(database_name).await?;
        postgresql.drop_database(database_name).await?;
    
        postgresql.stop().await
    }
  5. Get started with PostgreSQL Embedded

    main

    To use postgresql_embedded, you can initialize a PostgreSQL instance, set it up, and start the database process. The library supports both async and blocking APIs. By default, it downloads and installs PostgreSQL during runtime, but you can also enable a bundled feature to include the PostgreSQL archive directly in your binary at compile time.

    Key capabilities include:

    • Running PostgreSQL on ephemeral ports.
    • Unix socket support.
    • Fluent configuration via a settings builder.
    • URL-based configuration.
    • Support for installing PostgreSQL extensions.
    • Choice of native-tls or rustls (with AWS-LC or Ring).
    use postgresql_embedded::{PostgreSQL, Result};
    
    #[tokio::main]
    async fn main() -> Result<()> {
        let mut postgresql = PostgreSQL::default();
        postgresql.setup().await?;
        postgresql.start().await?;
    
        let database_name = "test";
        postgresql.create_database(database_name).await?;
        postgresql.database_exists(database_name).await?;
        postgresql.drop_database(database_name).await?;
    
        postgresql.stop().await
    }
  6. Configure PostgreSQL Embedded via the Settings Builder

    main
    The library provides a settings builder for fluent configuration and supports URL-based configuration. You can configure startup options, port settings (including ephemeral ports), and TLS preferences (native-tls or rustls with AWS-LC or Ring).
  7. Configure postgresql_extensions feature flags

    main

    The library uses feature flags to manage binary size and compile-time dependencies.

    TLS Features

    • tls-native-tls (Default): Enables Native TLS support.
    • tls-rustls-aws-lc-rs: Enables Rustls with the AWS-LC crypto provider.
    • tls-rustls-ring: Enables Rustls with the Ring crypto provider. To use Ring without compiling AWS-LC, disable default features and enable tls-rustls-ring.

    Repository Features

    These flags enable specific sets of PostgreSQL extensions:

    • portal-corp (Default): Enables PortalCorp PostgreSQL extensions.
    • steampipe (Default): Enables Steampipe PostgreSQL extensions.
    • tensor-chord (Default): Enables TensorChord PostgreSQL extensions.
  8. Configure PostgreSQL Embedded using SettingsBuilder

    main

    Use the SettingsBuilder to fluently construct a Settings object for your embedded PostgreSQL instance. This is the recommended way to customize connection details, installation paths, and server configurations.

    Commonly configured fields include:

    • host and port: TCP connection details.
    • username and password: Database credentials.
    • temporary: If true (default), the database is cleaned up on drop.
    • configuration: A map of server configuration options (e.g., max_connections).
    • socket_dir: Sets a Unix socket directory (Unix-only).
    use postgresql_embedded::SettingsBuilder;
    use std::path::PathBuf;
    
    let settings = SettingsBuilder::new()
        .host("127.0.0.1")
        .port(5433)
        .username("admin")
        .password("secret")
        .temporary(false)
        .build();
    
    // To configure a Unix socket:
    let socket_settings = SettingsBuilder::new()
        .socket_dir(PathBuf::from("/tmp/pg_socket"))
        .build();
  9. Manage an embedded PostgreSQL server with the `PostgreSQL` struct

    main

    The PostgreSQL struct is the primary entrypoint for managing an embedded PostgreSQL instance. It handles the full lifecycle of the database: installation from archives, initialization of the data directory, starting the server, and stopping it.

    Lifecycle Management

    • setup(): A high-level method that ensures the server is installed (extracting archives if necessary) and initialized (creating the data directory and configuration files).
    • start(): Starts the PostgreSQL server. If the port is set to 0, it will bind to a random available port.
    • stop(): Gracefully stops the server using pg_ctl in Fast shutdown mode.
    • drop(): Automatically stops the server if it is running. If the temporary setting is enabled, it also deletes the data directory, password file, and socket directory.

    Database Operations

    You can manage individual databases within the embedded instance using these methods:

    • create_database(name): Creates a new database.
    • database_exists(name): Checks if a database exists.
    • drop_database(name): Drops a database if it exists.

    Note: Database operations use a connection pool to the bootstrap database (the default superuser database) to execute commands.

    ```rust
    use postgresql_embedded::Settings;
    use postgresql_embedded::PostgreSQL;
    
    #[tokio::main]
    async fn main() -> Result<(), Box<dyn std::error::Error>> {
        let settings = Settings::default();
        let mut pg = PostgreSQL::new(settings);
    
        // 1. Install and initialize
        pg.setup().await?;
    
        // 2. Start the server
        pg.start().await?;
    
        // 3. Perform database operations
        pg.create_database("my_app_db").await?;
        
        if pg.database_exists("my_app_db").await? {
            println!("Database is ready!");
        }
    
        // 4. Stop the server
        pg.stop().await?;
    
        Ok(())
    }
    ```埋
  10. Execute PostgreSQL commands using PsqlBuilder

    main

    Use PsqlBuilder from postgresql_commands::psql to construct and execute PostgreSQL command line utility calls. You can configure the command string, host, port, username, and password using a builder pattern. The execute() method returns a tuple containing stdout and stderr as strings.

    use postgresql_commands::Result;
    use postgresql_commands::psql::PsqlBuilder;
    
    fn main() -> Result<()> {
        let psql = PsqlBuilder::new()
            .command("CREATE DATABASE \"test\"")
            .host("127.0.0.1")
            .port(5432)
            .username("postgresql")
            .pg_password("password")
            .build();
    
        let (stdout, stderr) = psql.execute()?;
        Ok(())
    }
  11. Use the Asynchronous API to download and extract PostgreSQL archives

    main

    The postgresql_archive crate provides an asynchronous API for fetching and extracting PostgreSQL binaries. You can use get_archive to retrieve a specific archive version based on a VersionReq and extract to unpack it into a target directory.

    To use this API, ensure you have an async runtime like tokio available.

    use postgresql_archive::{extract, get_archive, Result, VersionReq};
    use postgresql_archive::configuration::theseus;
    
    #[tokio::main]
    async fn main() -> Result<()> {
        let url = theseus::URL;
        let (archive_version, archive) = get_archive(url, &VersionReq::STAR).await?;
        let out_dir = std::env::temp_dir();
        extract(url, &archive, &out_dir).await
    }