Skip to content

Releases

pgman v0.5.0

Choose a tag to compare

@github-actions github-actions released this 02 Nov 14:57

Added

  • Encrypted dumps. encrypt = "kms:<key>" on an environment wraps a
    fresh key per dump with AWS KMS, bound to the dump's header; encrypt = "age:<public key>" encrypts to your own key, made by pgman key new.
    Masked and raw dumps are both encrypted, pg_dump's output piped straight
    into the encryptor; pgman restore opens them. The journal records
    what each copy is encrypted to, and pgman copies show says it.
    pgman key new|list makes and lists your age keys.

Changed

  • A protected environment's raw and masked dumps need encrypt.
    Until the environment says kms:, age: or none (masked copies
    only), they are refused, with the lines that settle it. Schema-only
    dumps, and protected targets that aren't environments, are unchanged
    (the latter dumped masked and unencrypted, which is said).
  • An encrypted raw dump is pgman's archive, a .pgman-dump directory,
    and its link <label>-latest.pgman-dump, not <label>-latest.dump:
    a script restoring the old link name finds the last unencrypted dump.
    Masked and raw copies of one label now share that link.

Fixed

  • Over MCP, the confirmation for dump, restore and apply_patches
    starts ticked. Claude Code kept showing the form again while its box
    was unticked, and the call failed after 300s with "no answer from the
    operator". Accept or Decline is the answer; a protected environment
    still takes its typed name.
  • Over MCP, dump and restore given a connection name only
    discovery knows (one connections lists from the project) say so,
    and to pass its URL or add it to connections.toml. They used to say
    there was no such name.
  • A restore's result says that owners and grants were left out, who
    owns what it restored, and how to grant another role (the
    application's) access: until then that role can read none of it.
  • A dump refused because its user can't read everything names the
    first 20 tables and sequences, then counts partitions under their
    parent (app.events: 106 partitions). It used to name the first 8
    and say "and 106 more", which could hide the few tables that decide
    whether --skip-unreadable is safe.
  • Masking rehashes a hash whose word isn't last
    (long_value_hash_lower_case), a bare hash on a cards or users
    table, and a card number's (ccnum_hash).
  • A key into a lookup that refines another (sub_religions into
    religions) is NULLed as one into the lookup is; a text-keyed lookup
    of categories keeps its key; a lookup's translations keyed by a
    language_id aren't refused.
  • The tap monitor's status says a sender's drop count is events
    "dropped in the app, never sent", not "JAR backpressure": OTLP
    senders never report one, and pgman's own drops are the footer's.
  • The tap's transactions and pools views drop numeric columns on a
    narrow screen instead of their text column: at 60 columns a row
    ended on a bare …, and at 80 the transactions view read con… and
    the pools view showed the pool under a POOL · APP header with no
    app. The pools header names the app only when a row has room for one.
  • A connection the server ends while a page of rows loads, a transaction
    commits or rolls back, or pgman is first connecting shows the
    connection-lost card with the server's reason, as a failed query's
    did. A failed :readonly on leaves the header as the session is.

pgman v0.4.4

Choose a tag to compare

@github-actions github-actions released this 31 Oct 13:05

Added

  • Masked dumps keep Odoo's models, views and menus whole (their names
    are JSON, which the defaults NULL, and the restored app wouldn't
    start). Its attachments, settings and mail servers are masked as before.

Fixed

  • A key into a lookup of personal answers (gender_id into genders,
    marital_status, country_of_birth) took the lookup's kept id, or
    was hashed alongside it, so the copy still joined to the answer. It is
    NULLed now, a rules-file null on it too, and where it can't be NULL
    the plan asks the rules file to decide. The lookup's own key is kept.
    Masked copies taken with an earlier version of a schema with such keys,
    key-value or settings tables, data columns, or the hashes below should
    be deleted (pgman copies rm, then by hand whatever it says it kept,
    and any dump file copied elsewhere).
  • Keycloak's session tables are dumped empty: their notes hold a
    device's address and details.
  • A key-value table's value is NULLed whatever its length: Keycloak's
    user_attribute.value (a user's phone or address) and
    realm_smtp_config.value (the SMTP password) were kept as bounded
    varchar. So is a column named for a bag of data (metadata,
    extra, raw_data).
  • A hash of a card, an account or a user (card_hash, account_hash,
    user_hash) is rehashed, not kept: there are few enough of each to
    try every one.

pgman v0.4.3

Choose a tag to compare

@github-actions github-actions released this 31 Oct 02:15

Added

  • Masked dumps keep Rails', Django's, Alembic's and Knex's migration
    tables whole, as Flyway's and Liquibase's are, and dump Django's
    sessions empty.

Fixed

  • A CHECK constraint that only bounds a column's length, an array's
    cardinality, or asks for a JSON object no longer stops a masked dump:
    the empty value and a hash pass it for certain. A bound on a text
    column's length is taken as its length, so a hash or a fake fits it.
    On GitLab's schema this takes pgman masking check from 850 problems
    to 87 real ones.
  • A domain's CHECK constraint (CREATE DOMAIN … CHECK (VALUE <> '')) is
    read too: a masked value it refuses stopped the restore part-way.
  • A unique column too short for the hash that keeps it unique is a
    problem however the hash came about. A NOT NULL unique column NULLed by
    default is hashed, and a short one passed the plan and lost its unique
    index on restore.
  • Masking keeps a date, time or boolean column named for a password or
    secret (password_changed_at, token_expires), which can't hold one.
  • Masking finds comment, description and similar words inside a folded
    camelCase name whose other words it doesn't know. A free-text word no
    longer masks a
    number, date or boolean (note_events, a failure_reason code), which
    can't hold free text.
  • Masking keeps settings named for a personal thing: a feature switched
    off (emails_disabled; a bare disabled is still special-category
    data), a theme, colour or mode (login_theme, mobile_view_mode; not
    an eye, skin or hair colour), a bounded template, layout or style, and
    a boolean about a signature. An email's subject or body is NULLed
    as free text, not given a fake address.
  • Masking knows more personal words, found by running it over GitLab's,
    Keycloak's, Odoo's, Synapse's and AdventureWorks' schemas: an
    identification_id (a national ID number), a visa or work permit, an
    abbreviated acc_number, a bonus or commission, social handles
    (linkedin, twitter, …), OAuth device and user codes, an access key,
    sick leave, pronouns, a login or external id (extern_uid), a
    reviewer's or attendee's name, a partner's or guest's name, reply_to,
    an sms_number, a browser, a TLS fingerprint (ja3). A word with
    digits in it (ipv4, last4) now matches. A hash of something
    personal and easy to guess (holder_name_hash, last_digits_hash,
    email_digest) is hashed again, so it still joins; any other *_hash
    is kept, and the rules file's [classify] words reach both.
  • pgman copies rm says what to do when it can't drop a copy restored
    through a URL with the password in it: drop it by hand, or restore
    through a connections.toml entry with password_env.

pgman v0.4.2

Choose a tag to compare

@github-actions github-actions released this 30 Oct 14:50

Fixed

  • Masked dumps judge a camelCase column name that Postgres folded to
    lower case by its words: an unquoted dateOfBirth is stored
    dateofbirth, which pgman read as one unknown word and kept. On such
    a schema a masked dump kept dates of birth, phone and mobile numbers,
    email addresses, password hashes, bank sort codes and account numbers,
    passport and social security numbers, and special-category columns
    such as sexual orientation. Run pgman masking show again on a schema
    named this way; copies taken with 0.4.0 or 0.4.1 should be deleted
    (pgman copies rm). A phrase such as "account number" now matches
    accountnumber too, and "social security", "building society" and
    "account name" are known.
  • pgman masking check, and a masked dump, stop at a masked column a
    validated CHECK constraint names when its masked value may fail it
    (wage > 0 on a nulled NOT NULL wage became 0): the restore stopped
    part-way at the first such row, after the whole dump.
  • pgman copies rm names the database it keeps and why; one restored
    through a URL with a password in it said only "invalid configuration:
    password missing".

pgman v0.4.1

Choose a tag to compare

@github-actions github-actions released this 29 Oct 21:48

Fixed

  • Slow queries work on PostgreSQL 12, which names pg_stat_statements'
    times total_time and mean_time.
  • Masked dumps work on PostgreSQL 10 and 11.
  • A restore into a database it created counts "already exists" from
    the new database's template as harmless and names it, instead of
    saying the database wasn't empty: every restore into YugabyteDB
    exited 1 for its own foreign server.
  • The schema wizard and schema_lint leave alone what is in a schema
    an extension made, such as TimescaleDB's chunks.
  • A masked dump of a database with TimescaleDB hypertables stops and
    names them, instead of restoring them as plain tables without the rows
    of their compressed chunks.
  • The schema wizard's "foreign key without an index" (LINT101) no
    longer flags a key whose column leads an index: it read the index's
    second column.

pgman v0.4.0

Choose a tag to compare

@github-actions github-actions released this 29 Oct 15:31

Added

  • Masked dumps: pgman dump --masked, the default for a protected
    environment, takes a copy with what looks personal replaced as it is
    read — emails, names, phones, postcodes, dates of birth, IPs and
    passwords by realistic fakes, secrets, special-category data,
    identifiers, addresses and free text removed — judged from each
    column's name and type, with framework presets (Flyway, Liquibase,
    Envers, Spring Session). Foreign keys keep joining; a fresh key per
    copy, never sent to the server. One snapshot, a .pgman-dump
    directory with checksums, which pgman restore loads rows-then-keys.

  • .pgman/masking/<database>.toml: the team's own rules (keep with a
    reason, fixed, hash, email, name, phone, ip, postcode, year,
    date.shift, rows dropped), and pgman masking show / check to see
    and test them from the catalog alone, in CI too.

  • docs/privacy.md: what pgman does with personal data, for a client's
    DPO.

  • pgman copies: every dump taken on this machine, from a journal
    pgman now keeps (~/.local/share/pgman/journal.jsonl): when, of what,
    raw or schema, why, the file, when it expires, where it was restored.
    copies show gives one in full, copies rm deletes one (and repoints
    its -latest link), copies report prints the whole account as text,
    markdown or JSON. No data, connection details or secrets are recorded.

  • A dump policy per environment in environments.toml: dump
    (unmasked, masked, schema, never), dump_ttl, dump_reason
    and unattended. A project's .pgman/pgman.toml can lower an
    environment's ([dumps.environments.<name>] dump = "schema"), never
    raise it.

  • journal = { group = "/pgman/dumps" } on an environment sends the
    journal's events for its copies to that CloudWatch Logs group with the
    aws CLI, as the IAM identity that sent them: file names without
    paths, no server addresses. A send that fails never fails the dump and
    is retried by the next pgman dump or pgman copies; each event is
    sent once.

  • pgman dump --schema-only, --reason and --ticket. The reason
    prompt offers the ticket in the branch's name (feature/APP-101-…),
    else the last reason given; [dumps] ticket_url links tickets in
    pgman copies.

Changed

  • A dump of a protected environment asks why it is taken, and a raw
    dump of one (every row) also takes the environment's name typed,
    after a warning that it copies personal data to this machine. Without
    a terminal, a raw dump of a protected environment is refused unless
    the environment says unattended = true in your own
    environments.toml; scripts that need only the structure use
    --schema-only --reason …. Other environments are dumped as before.
  • A protected environment is dumped masked by default; a raw copy of
    one needs dump = "unmasked" on it in your own environments.toml,
    --unmasked, and its name typed at a terminal.
  • A dump with no FILE goes to ~/.local/share/pgman/dumps rather than
    the current directory. A raw dump of a protected environment is
    refused in iCloud Drive, Dropbox, OneDrive or Google Drive; every dump
    is excluded from Time Machine.
  • pgman copies rm also drops the databases restored from a copy (only
    local, unprotected ones whose comment names it).
  • MCP's dump takes a protected environment masked (or schema only),
    using a rules file only when it is committed and you have confirmed it
    at a terminal; a raw dump of one over MCP, which v0.3 allowed after its
    name was typed, is refused, and every MCP tool refuses a database
    restored from one. The reason is given by you in the confirmation.
    Each MCP dump and restore is journaled.
  • environments.toml has new keys (dump, dump_ttl, dump_reason,
    unattended): an older pgman reading a file that uses them refuses
    it as unknown keys.
  • MCP's restore takes a masked dump, or a dump this machine's pgman
    took; a dump the journal doesn't know, or a raw copy of a protected
    environment (copied or moved too: each dump's fingerprint is
    recorded), is refused, yours to restore at a terminal.
  • A restore that fails part-way is journaled, and the database it made
    is marked as the copy's from the moment it exists.
  • A dump of a database restored from a raw copy of a protected
    environment is treated as a dump of that environment.

Fixed

  • pgman … | head (any command whose reader goes away early) ends
    quietly instead of aborting.
  • pgman restore into a protected environment of replicas only says it
    is protected, not to name or add its primary.

pgman v0.3.1

Choose a tag to compare

@github-actions github-actions released this 28 Oct 21:18

Changed

  • The Homebrew formula depends on libpq, so pgman dump and pgman restore work from a brew install with nothing else installed or
    linked: its wrapper tells pgman where libpq's pg_dump and
    pg_restore are (PGMAN_PG_BIN).
  • dump and restore look for the tools in --pg-bin, then
    $PGMAN_PG_BIN, then Homebrew's libpq, then PATH, and say which
    they ran. Homebrew's libpq now comes before PATH, so an older
    PostgreSQL on PATH doesn't win over it.
  • A pg_dump older than the server says brew upgrade libpq too, since
    the libpq found may be the stale one.

pgman v0.3.0

Choose a tag to compare

@github-actions github-actions released this 28 Oct 19:45

Highlights

  • A local copy of an environment in two commands. pgman dump staging/ledger takes it from the replica through the bastion, the
    password read at that moment and never on a command line or on disk;
    pgman restore … local --as ledger_copy --clean puts it into a local
    database, never a protected one.
  • Scripts and patches from the command line. pgman run runs a
    script that manages its own transactions, \c between databases;
    pgman apply is the environments view's P, --outside included.
  • For a coding agent: MCP dump and restore, each confirmed by
    you, so an agent can refresh its copy without ever holding the
    environment's credentials.

Added

  • pgman dump <target> [FILE]: a dump by your own pg_dump, through
    pgman's connection — the environment's replica unless --primary, the
    password read from its secret at that moment and given to pg_dump only
    in its environment, the ssh tunnel opened for it. The local pg_dump's
    version is checked against the server first; the dump is written to a
    hidden name and renamed into place on success, private to you, with a
    -latest link for a default name. A replica cancelling the dump (a
    recovery conflict) and a permission failure are explained. What the
    dump's user can't read (no SELECT, no USAGE on its schema, row-level
    security, large objects) is found before pg_dump starts and refused
    by name; --skip-unreadable leaves exactly that out and says so. A
    sequence that a readable table owns can't be left out, and is named
    with the GRANT that fixes it. See
    docs/dump-restore.md.
  • pgman restore <FILE> <target>: a dump restored by your own
    pg_restore, never into a protected environment and only on this
    machine unless --allow-remote. --as NAME restores into another
    database, --create makes it, --clean replaces it; other sessions
    refuse it unless --terminate; it is confirmed, or --yes. It exits
    1 when pg_restore went past real errors, counting a newer pg_dump's
    SET of a setting the server doesn't have as harmless. --jobs,
    --keep-owners and options after -- go to pg_restore; a plain SQL
    dump is psql's.
  • pgman run <target> <FILE>: a script that runs its own transactions
    (BEGIN … COMMIT), as psql -f does, with \c DATABASE moving it
    to another database of the environment. Checked against safety.toml
    first; confirmed (a protected target by typing its name); a failure
    or Ctrl-C reports what stayed committed, what rolled back and what
    never ran. VACUUM and CONCURRENTLY run without the statement
    timeout; a standby is refused. See docs/run.md.
  • pgman apply <target> [--outside] [--dry-run | --copy]: the
    environments view's P from the command line. An --outside plan or
    a protected environment takes the typed name, which --yes doesn't
    answer.
  • MCP: dump and restore tools, each confirmed by the operator
    through elicitation (a protected environment, or a database to be
    replaced, by typing its name); files stay inside the server's
    working directory, and a restore is local and unprotected only.
  • A <target> on the command line is ENV, ENV/DATABASE,
    ENV/SERVER/DATABASE, a connections.toml name, or a URL. It is
    protected by its environment, by "prod" in its name, or by being on a
    server a protected environment reaches, however that is written.
  • pgman dumpp says unknown command "dumpp" (did you mean dump?).
  • Ctrl-C, SIGTERM and SIGHUP stop dump, restore, run and apply
    cleanly: the tool stopped, a partial file removed, a transaction
    rolled back.

Fixed

  • Ctrl-C in the editor (and Esc twice while patches run) now cancels
    the running statement on a server reached with sslmode=require or
    verify-*, such as RDS: the cancel request went out without TLS,
    which the client library refuses for such a session, so it never
    reached the server.

pgman v0.2.1

Choose a tag to compare

@github-actions github-actions released this 25 Oct 13:33

Fixed

  • The help overlay names the JDBC tap and the NOTIFY panel by :tap /
    :notify first, with F4 / F3 beside them, and the start card
    does the same for the tap: on a Mac terminal the F-keys often never
    reach pgman. The section list's teasers lead with their key
    (:tap / F4 · your app's statements, live, S · schemas, …), so it
    survives at 60 columns, and each fits whole at 80; the tap's ended
    (:… at 80x24.
  • The result diff's title names its key (key: id (primary key)) before
    the A and B query text, so an 80-column terminal no longer cuts it
    off; a title too long for its border ends on …. A diff of results
    with different columns says columns differ in its title and under
    the counts, which read as a fault (key: full row, every row added
    and removed) without it.
  • :fixture and Y on a partial result end their status on "fetched
    rows only, more not fetched", which the footer keeps whole; at 160
    columns it was cut mid-note, along with the file name.
  • The schema wizard's detail pane wraps between words (it broke
    referen / ces at 80x24), and its list sizes the object column to
    leave the title room: at 80 columns the title column was three
    letters wide and its header read tit.
  • A result column's header squeezed with its column ends on …, as
    its cells do, and keeps its sort marker: at 80x24 billing_email
    read billing_ over cells reading billing….
  • The result title is fitted to its border: with a filter on and
    five-digit counts, an 80-column title ran into the corner mid-word;
    it now drops whole segments from the end and ends on ….
  • The JDBC tap's views cut a long cell with … rather than at the
    border: N+1 and callers keep the caller whole and add the fingerprint
    only when there is room to read one, hotspots' fingerprint · caller
    and transactions' txn · last sql fit their column (at 80x24 the
    hotspots header read FINGERPRINT · LAST CA over · InvoiceS), and
    any row or header too wide for the panel ends on ….
  • The NOTIFY panel cuts a long payload, and its title's key hints, with
    … at the border rather than mid-token, and shows a payload's
    newline as ⏎ on its row.
  • The start card's two key columns line up: :tap JDBC tap (F4)
    started four columns right of the S / T / L / ctrl-t above
    and below it, as ctrl-l's key is wider than the others. At 100
    columns the card takes the short ctrl-l wording so
    S schema browser (26 tables) fits, and a right-hand hint too long
    for a narrower card ends on … rather than at the border.
  • A start card too short for every key hint keeps its ? all keys
    line and says how many it left off (+3 more); at 60x16 the hints
    went with nothing to say so.
  • The README lists cell editing (u) and paging (M), and its links
    to docs/ and the licence files are absolute, so they resolve on
    crates.io too; it says to run brew trust tombaldwin/tap first on
    Homebrew 6 or newer.
  • --demo no longer writes pgman.log or creates pgman's cache,
    config and data directories, as its "no disk writes" promised, and
    \fixture / \report with no path write nothing to the cache
    dir: they say --demo writes no files and ask for a path.
  • docs/tap.md says the monitor's "JAR backpressure" drop count comes
    from TCP and UDP senders only, and never appears with --tap-otlp.
  • A connect no longer hangs at "connecting…" behind another session's
    ACCESS EXCLUSIVE lock: the schema fetch skips the sizes and indexes
    the lock covers, the connect's own catalog reads give up after a 2 s
    lock_timeout (the session's own setting is restored), and the status
    line says the schema is incomplete, names the holder's pid first (a
    narrow footer cut it off) then the lock — for a read that gave up
    behind a lock on a catalog too — and how to reload. The schema
    browser says size: not fetched (locked) and indexes: not fetched
    for such a table, which read as a table with no indexes. A reload (S
    then r, or the refresh after DDL) is bounded the same way, puts back
    your own lock_timeout with nothing of yours running in between, and
    no longer says "schema reloaded" over a read the server cancelled: any
    failed catalog read is named. Inside an open transaction it reads
    nothing and says so, since a read the server cancelled there would
    abort your transaction.
  • A session the server ends under a running statement
    (pg_terminate_backend, a crash shutdown) shows the server's reason
    on the connection-lost card, not "the connection to the server closed"
    with the reason in the footer; a session lost while still connecting
    keeps its reason too. The server's hint comes with it: a hot-standby
    conflict's says to reconnect and repeat the command.
  • The header no longer says Session: writable on a standby whose
    connect-time probe went unanswered: when a paged run's probe finds the
    server in recovery, the session is pinned read-only there, in the
    header and on the server, as the connect would have pinned it; nor
    after a :readonly on on a standby that failed to apply, or a
    :readonly off the server took just as the standby was pinned.
  • A primary is no longer pinned read-only as "a read replica" when a
    schema ahead of pg_catalog on the search path has a
    pg_is_in_recovery() of its own: the connect's probe, and MCP's,
    the dashboard's and the advisor's, call pg_catalog's. So do
    apply_patches' statement_timeout restore and the sessions panel's
    terminate, which a shadow could make report a session ended that
    wasn't.
  • Under a read-only session the confirm card no longer says Postgres
    will refuse a statement pgman can't classify, such as SET, which it
    runs: it says Postgres refuses it if it writes.

Changed

  • The header and About box say the date beside the version is the
    release date: pgman 0.2.0 · released 2026-10-23. At 60
    columns the header now shows the version alone.

pgman v0.2.0

Choose a tag to compare

@github-actions github-actions released this 24 Oct 10:34
c719886

Highlights

  • Edit a cell in place. u on a result cell writes a one-row
    UPDATE by primary key, shown in the same safety confirm as any
    statement you run.
  • Page past 1000 rows. M fetches the next 1000 rows of a
    SELECT, through a server-side cursor that pgman closes after 60 s
    idle, 10 min open, or before any other work; a standby holds none.
  • A config advisor in the schema wizard (W). Read-only findings
    on cache hit ratio, temp-file spills, dead tuples, lock waits and
    checkpoints, each with its evidence, a conservative setting and the
    RDS / Aurora parameter to change.
  • The whole JDBC tap event. Enter on a tap row shows the full
    SQL, every bound parameter, the error's cause chain and the caller
    stack; the list shows the focused statement's parameters beneath it.
  • Help in sections. One section at a time instead of one long page,
    with a summary for each and a filter across them all.
  • More logs read. Logs → SQL now reads Postgres's csvlog and
    jsonlog, p6spy and datasource-proxy logs, and statements that
    failed with an ERROR:.
  • sslrootcert in a connection string pins the trusted CAs, as in
    libpq.

Added

  • Cell editing: u on a cell in the grid (or a field in row detail)
    edits it in place. Enter writes a one-row UPDATE … WHERE <primary key> and sends it through the safety guard like any editor statement,
    the statement shown in the confirm; Ctrl-N sets NULL (and NULL ↔ the
    empty string). It works on a SELECT from one table with its primary
    key in the result, and otherwise says why not (a join, a view, no
    primary key, the key not selected, a computed column, read-only). The
    UPDATE changes exactly one row or is rolled back, and the cell then
    shows the value as the server stored it. Before it commits, pgman
    checks the server still calls the table, the column and the key what
    pgman's schema cache does; a column renamed since pgman read the
    schema (and another added under its old name) is rolled back with
    the table changed since pgman read it, never written to the wrong
    column.
  • M in the results fetches past the first 1000 rows. A plain SELECT
    from the editor now reads through a server-side cursor; while it has
    more, the title says partial · M fetches more, and each M appends
    1000 rows (up to 100 000 held), keeping the sort and filter. While
    the cursor is open the session is idle in transaction, holding an
    ACCESS SHARE lock on the tables the query reads: DDL on them (a
    migration, ALTER, TRUNCATE, VACUUM FULL, REINDEX) waits behind
    it, and later statements on those tables queue behind that DDL. pgman
    closes the cursor after 60 s without a fetch or 10 min open (sooner if
    the server's own timeouts are), before any other database work, and
    at the end of the result; M then re-runs the query. On a standby
    no cursor is held at all (a recovery conflict would end the session):
    the first page commits at once and M re-runs the query for more.
    EXPLAIN, scripts, \watch, locking SELECTs, a SELECT that calls
    a function other than a short list of read-only built-ins (count,
    coalesce, lower, now, …) — or one of those names that a schema
    has its own function for — and a SELECT inside an open
    transaction stay capped at 1000 as before.
  • Config advisor in the schema wizard (W): read-only findings from the
    server's statistics — buffer cache hit ratio, temp-file spills (with the
    spilling statements from pg_stat_statements), dead tuples and the
    autovacuum setting behind them, lock-wait chains, and requested
    checkpoints (pg_stat_checkpointer from PG17), blamed on
    max_wal_size only when pg_stat_wal shows the WAL to have forced
    them. Each shows its
    evidence, how long the counters have accumulated, a conservative
    setting, and its RDS / Aurora parameter-group name, static or dynamic.
    e yanks a finding's evidence query.
  • Enter on a JDBC tap List row opens the whole event: the full SQL,
    pool / conn / txn, every bound parameter, the error's cause chain
    down to the root cause, and the caller stack. A failed row used to be
    only coloured red, and only the innermost caller frame reached the
    Callers view.
  • The JDBC tap's List view shows the focused statement's bound
    parameters beneath the list, labelled params (some redacted) when
    the producer masked a value and params (withheld by source) for
    OpenTelemetry spans, which carry none.
  • sslrootcert=<file> in a connection string pins the trusted CAs, as
    in libpq (and require with a CA file checks the chain). It used to
    be ignored, with the default trust store used instead. sslcrl and
    sslcrldir are refused rather than ignored.
  • A Postgres log's ERROR: + STATEMENT: pair is reconstructed as a
    query marked failed with the error message — what the default
    log_min_error_statement logs with nothing else on. A statement
    log_statement already logged is marked failed rather than listed
    twice. The log picker marks a failed pick ✗ and shows its error.
  • Logs → SQL reads Postgres's csvlog and jsonlog (PG 15+) formats as
    well as stderr: same pairing of parameters and errors, by pid.
  • Logs → SQL reads p6spy (default single-line format) and
    datasource-proxy query logs, tagged [p6spy] / [ds-proxy] in the
    picker.
  • Tab after :help completes the help section names, as it completes
    command names.
  • docs/tap.md: the JDBC tap for pgman users — the OpenTelemetry, TCP
    and UDP ways in, the v1 event fields, the limits and drop counts,
    --tap-record / --tap-replay, redaction and security.

Changed

  • Ctrl-C in the results cancels a running query, as it does in the
    editor, instead of arming quit; with nothing running it still asks
    for a second press.
  • Y and :fixture say fetched rows only when the result has more
    rows than pgman holds.
  • The result diff (D) matches rows on the table's real primary key
    when A and B are the same columns from one table and the key is in
    both (key: id (primary key) in the title), instead of the leftmost
    column that happens to be unique; otherwise it infers as before.
  • Help is a list of sections and one section at a time instead of one
    ~275-row page: from 110 columns the list sits beside the section, and
    on a narrower terminal the list comes first, each section with a
    one-line summary, and Enter opens one. Tab / ] / [ move between
    sections, / filters every section live (matching rows under their
    headings), and :help all keeps the single page. :help <topic>
    takes any section name or the start of one, and an unknown topic lists
    every section.
  • One size format everywhere: database sizes on the start card and in
    the environments overview, and table sizes in the schema browser, read
    9.0 MB / 212 MB / 1.2 GB. The start card used pg_size_pretty,
    which stays in kB up to 10 MB (10199 kB beside 10 MB), and the
    schema browser used MiB with two decimals.
  • In the editor and the prompts the error footer points at F2 / :error detail, not F2 alone: F2 takes fn on a Mac.
  • The start card's recent list says how to bring an entry back: in the editor, ctrl-p recalls · ctrl-r searches.
  • Loading a saved query (after its :param values, if any) ends the
    status with the key that runs it: ⏎ runs it, or alt-⏎ runs it when
    the statement has no ; and Enter would be a newline.
  • The tab bar's hint reads [ ] prev/next tab instead of ] [ switch,
    which looked like a stray pair of brackets.
  • One TLS crypto provider (ring) instead of two: no cmake / C build of
    aws-lc, and a smaller binary (x86_64 macOS 11.9 MB to 9.1 MB). The
    Postgres connection no longer offers the post-quantum X25519MLKEM768
    key exchange, which only the aws-lc provider had.
  • Logs → SQL: a Postgres FATAL: or PANIC: followed by its
    STATEMENT: (a read replica's terminating connection due to conflict with recovery) is reconstructed as a failed query, like an
    ERROR:'s.

Fixed

  • :readonly on under a writable profile could be undone from the
    editor: SET default_transaction_read_only = off got the other
    guard's confirm, and after y the session wrote while the header still
    said read-only. A session read-only by :readonly on or a standby's
    pin now blocks that SET (and BEGIN READ WRITE, set_config(…)) as
    a read-only profile does, and says :readonly off is the way out.
  • A connection that dropped under a running pgman — a session the server
    ended (pg_terminate_backend, a restart, an idle timeout) or a network
    that went — left the header saying connected, and every later command
    failed with connection closed; the log said only postgres connection error: db error. pgman now shows the connection-failed screen, with
    connection lost and the server's own message (terminating connection due to administrator command), and r reconnects; the log keeps the
    message and its SQLSTATE.
  • A long pattern typed into a footer prompt (the schema browser's and
    help's / filter, the grid's / filter and f find, ctrl-r
    history search) no longer loses its filter: / / find: head or the
    cursor: the head stays, the pattern shows its end after a …, and
    the cursor sits after it. The key hints go first.
  • The JDBC tap monitor read "no events yet", and the footer showed no
    TAP badge, while only commits / rollbacks had arrived; the status
    now counts them (2 commits/rollbacks).
  • The schema wizard, slow queries, sessions and notifications lists,
    and the JDBC tap's List, Hotspots, Callers, Transactions, N+1 and
    Baseline views, scrolled as if their header row were a list row, so
    moving the cursor past the bottom left the focused row hidden one
    line below the edge.
  • After SELECT … FROM ONLY t, I yanked an INSERT INTO public.only:
    ONLY was read as the table's name. It...
Read more