Rust-Postgres

repository·master·Indexed 26 days ago

https://github.com/rust-postgres/rust-postgres

Native PostgreSQL client implementations for Rust, providing both synchronous (postgres) and asynchronous (tokio-postgres) interfaces. The project includes crates for type conversions (postgres-types), procedural macros for implementing FromSql and ToSql traits (postgres-derive), and TLS support via postgres-native-tls and postgres-openssl.

Tokens
23.2K
Snippets
42
Records
177
Agent score
86%

What's inside rust-postgres

  1. Choose a PostgreSQL client for Rust

    master

    Rust-Postgres provides two primary client implementations depending on your concurrency model:

    • postgres: A native, synchronous PostgreSQL client.
    • tokio-postgres: A native, asynchronous PostgreSQL client.

    Additional crates are available for type conversions (postgres-types) and TLS support (postgres-native-tls or postgres-openssl).

  2. Set up the test suite using Docker

    master

    To run the project's test suite, you must have a PostgreSQL instance running. The recommended method is using docker-compose.

    Prerequisites (Ubuntu):

    1. Install Docker and Docker Compose: sudo apt install docker.io docker-compose.
    2. Ensure your user has permissions: sudo usermod -aG docker $USER.

    Execution Steps:

    1. Navigate to the top-level directory of the rust-postgres repository.
    2. Start the database: docker-compose up -d.
    3. Run the tests: cargo test.
    4. Stop the database: docker-compose stop.
    # Setup and run tests
    sudo apt install docker.io docker-compose
    sudo usermod -aG docker $USER
    docker-compose up -d
    cargo test
    docker-compose stop
  3. Use postgres-protocol for low-level Postgres communication

    master

    The postgres-protocol crate provides low-level components for the Postgres communication protocol, including message and value serialization/deserialization.

    Warning: This crate is designed as a building block for higher-level APIs like rust-postgres and is not intended for direct use by most developers. It assumes the client_encoding backend parameter is set to UTF8. If it is not, the library may not behave correctly.

  4. Manage PostgreSQL transactions with `Transaction`

    master

    The Transaction struct provides a synchronous handle for managing PostgreSQL database transactions.

    Key Behaviors:

    • Implicit Rollback: Transactions will automatically roll back by default when they are dropped.
    • Explicit Commit: You must call .commit() to persist changes made within the transaction.
    • Explicit Rollback: You can call .rollback() to discard changes and receive any error encountered during the process.
    • Nesting: Transactions can be nested. Calling .transaction() or .savepoint() on an existing Transaction creates a nested transaction using PostgreSQL savepoints.
  5. Use `MakeTlsConnector` for TLS support in `tokio-postgres` or `postgres`

    master

    To enable TLS connections using OpenSSL, use the MakeTlsConnector struct. This requires the runtime feature to be enabled. You can create a connector from an openssl::ssl::SslConnector and pass it to the connect function of either tokio_postgres or postgres.

    use openssl::ssl::{SslConnector, SslMethod};
    # #[cfg(feature = "runtime")]
    use postgres_openssl::MakeTlsConnector;
    
    # fn main() -> Result<(), Box<dyn std::error::Error>> {
    # #[cfg(feature = "runtime")] {
    let mut builder = SslConnector::builder(SslMethod::tls())?;
    builder.set_ca_file("database_cert.pem")?;
    let connector = MakeTlsConnector::new(builder.build());
    
    let connect_future = tokio_postgres::connect(
        "host=localhost user=postgres sslmode=require",
        connector,
    );
    # }
    
    // ...
    # Ok(())
    # }
  6. Configure TLS support in postgres

    master

    TLS support is implemented via external libraries. Client::connect and Config::connect require a TLS implementation as an argument.

    • No TLS: Use postgres::NoTls when encryption is not required.
    • OpenSSL: Use the postgres-openssl crate for OpenSSL-backed TLS.
    • Native TLS: Use the postgres-native-tls crate for native TLS implementations.
  7. Use MakeTlsConnector for tokio-postgres or postgres

    master

    To enable TLS support in tokio-postgres or postgres using the native-tls crate, use MakeTlsConnector. This requires the runtime Cargo feature to be enabled. You can wrap a standard native_tls::TlsConnector into a MakeTlsConnector and pass it to the connection function.

    use native_tls::{Certificate, TlsConnector};
    # #[cfg(feature = "runtime")]
    use postgres_native_tls::MakeTlsConnector;
    use std::fs;
    
    # fn main() -> Result<(), Box<dyn std::error::Error>> {
    # #[cfg(feature = "runtime")] {
    let cert = fs::read("database_cert.pem")?;
    let cert = Certificate::from_pem(&cert)?;
    let connector = TlsConnector::builder()
        .add_root_certificate(cert)
        .build()?;
    let connector = MakeTlsConnector::new(connector);
    
    // For tokio-postgres:
    let connect_future = tokio_postgres::connect(
        "host=localhost user=postgres sslmode=require",
        connector,
    );
    # }
    
    // For postgres (synchronous):
    // let client = postgres::Client::connect(
    //     "host=localhost user=postgres sslmode=require",
    //     connector,
    // )?;
    # }
    # Ok(())
  8. Use the synchronous postgres client

    master

    The postgres crate provides a synchronous client for PostgreSQL. It is a lightweight wrapper around tokio-postgres that blocks on futures using a tokio runtime.

    To connect, use Client::connect with a connection string and a TLS implementation. If you do not require TLS, use NoTls.

    Common operations include:

    • batch_execute: Run multiple SQL statements at once.
    • execute: Run a statement and return the number of rows affected.
    • query: Run a statement and return a list of Row objects.
    • row.get(index): Retrieve a value from a specific column in a row.
    use postgres::{Client, NoTls};
    
    fn main() -> Result<(), postgres::Error> {
        let mut client = Client::connect("host=localhost user=postgres", NoTls)?;
    
        client.batch_execute("
            CREATE TABLE person (
                id      SERIAL PRIMARY KEY,
                name    TEXT NOT NULL,
                data    BYTEA
            )
        ")?;
    
        let name = "Ferris";
        let data = None::<&[u8]>;
        client.execute(
            "INSERT INTO person (name, data) VALUES ($1, $2)",
            &[&name, &data],
        )?;
    
        for row in client.query("SELECT id, name, data FROM person", &[])? {
            let id: i32 = row.get(0);
            let name: &str = row.get(1);
            let data: Option<&[u8]> = row.get(2);
    
            println!("found person: {} {} {:?}", id, name, data);
        }
        Ok(())
    }
  9. Use pipelining to improve query performance

    master

    tokio-postgres supports pipelining, which allows multiple independent queries to be sent to the server without waiting for the previous one to complete. This minimizes time spent waiting for network round-trips.

    Pipelining occurs automatically when you poll multiple query futures concurrently, for example, by using the futures::future::try_join combinator.

    use futures_util::future;
    use std::future::Future;
    use tokio_postgres::{Client, Error, Statement};
    
    async fn pipelined_prepare(
        client: &Client,
    ) -> Result<(Statement, Statement), Error>
    {
        future::try_join(
            client.prepare("SELECT * FROM foo"),
            client.prepare("INSERT INTO bar (id, name) VALUES ($1, $2)")
        ).await
    }
  10. Connect to a PostgreSQL database with Client

    master

    Use Client::connect to establish a synchronous connection to a database using a configuration string and a TLS mode. Alternatively, use Client::configure to obtain a Config object for more granular setup.

    use postgres::{Client, NoTls};
    
    let mut client = Client::connect("host=localhost user=postgres", NoTls)?;
  11. Run a PostgreSQL container using docker-compose

    master

    The repository provides a docker-compose.yml file to spin up a PostgreSQL instance for development or testing. It uses the postgres:18 image, maps port 5433 on the host to 5433 in the container, and initializes the database using a script located at ./docker/sql_setup.sh. The default password for the postgres user is set to postgres via the POSTGRES_PASSWORD environment variable.

    version: '2'
    services:
      postgres:
        image: docker.io/postgres:18
        ports:
          - 5433:5433
        volumes:
          - ./docker/sql_setup.sh:/docker-entrypoint-initdb.d/sql_setup.sh
        environment:
          POSTGRES_PASSWORD: postgres