Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

1. Connecting

In this chapter we’ll use Config, Session::connect, and the background driver task that keeps every call you make cancellation-safe.

Setup

use babar::{Config, Session};

#[tokio::main(flavor = "current_thread")]
async fn main() -> babar::Result<()> {
    let cfg = Config::new("localhost", 5432, "postgres", "postgres")
        .password("postgres")
        .application_name("ch01-connecting")
        .connect_timeout(std::time::Duration::from_secs(5));

    let session: Session = Session::connect(cfg).await?;        // type: Session
    println!(
        "server_version = {}",
        session.params().get("server_version").unwrap_or("?"),
    );
    session.close().await?;
    Ok(())
}

Config is a struct, not a string

Config::new(host, port, user, database) takes the four required fields by position. Optional fields are added by chained methods — .password(...), .application_name(...), .connect_timeout(...), TLS settings, and so on. Because Config is a plain struct you can build it from any source you like (env vars, a config file, a clap::Parser); babar deliberately doesn’t ship a DSN parser or a Config::from_env(). Connection details should be visible and explicit in code.

What Session::connect actually does

Session::connect(cfg) opens one TCP connection to Postgres, negotiates TLS if you asked for it, runs the SCRAM-SHA-256 handshake, exchanges startup parameters, and hands you back a Session. From that moment on, the Session is a thin handle: the real socket ownership lives in a background Tokio task that the Session spawns.

That background task is the reason every public call on Session is cancellation-safe. If you tokio::select! away from a query midway through, the protocol stays in a consistent state — the driver task finishes reading the in-flight messages even if you don’t await the result. The shape of the model is sketched in What makes babar babar; we dive into the details in explanation/driver-task.md.

Reading server parameters

#![allow(unused)]
fn main() {
let v = session.params().get("server_version").unwrap_or("?");
let tz = session.params().get("TimeZone").unwrap_or("?");
println!("server_version={v}, TimeZone={tz}");
}

session.params() returns the ParameterStatus map Postgres sent during startup. It’s read-only and updated by the server when it issues a ParameterStatus message.

Closing politely

session.close().await sends a Terminate and waits for the driver task to drain. If you drop the Session without calling close, the background task is still cancelled cleanly — but close lets you observe a final Result if the server objected to anything.

Recovering when the server is unreachable

Session::connect returns babar::Result<Session>. The error is the same babar::Error enum reviewed in Chapter 9; for connection failures you’ll typically see Error::Io(_) (DNS, TCP, TLS) or Error::Server { code, .. } (auth rejected, database missing). Inspect the variant directly — there’s no Error::kind() classifier.

Next

Chapter 2: Selecting walks through reading rows back into typed Rust values.