Re: [PATCH] Add PROMPT_COMMAND and dynamic prompt support to psql - Mailing list pgsql-hackers

From Kirk Roybal
Subject Re: [PATCH] Add PROMPT_COMMAND and dynamic prompt support to psql
Date
Msg-id CAE==DwevFQkHCBzXRrwtGj4eLc-QCG_gUctqONqPvv7abC2ZMg@mail.gmail.com
Whole thread
In response to [PATCH] Add PROMPT_COMMAND and dynamic prompt support to psql  (Kirk Roybal <kirk.roybal@gmail.com>)
List pgsql-hackers
Hi,

v2 follow-up: one-page merge rationale, explicit review ask, and a TAP test
(as offered in the original post). Patch attached.

========================================================================
Why merge this (one page)
========================================================================

Problem
-------
Interactive psql prompts are static between PROMPT1/PROMPT2 redefinitions.
Users who want connection context, transaction state, or last-command status
in the prompt today either fork psql or abuse %`shell` on every redraw. There
is no bash-shaped hook to regenerate the prompt once per readline.

Bash-shaped solution
--------------------
Mirror bash, not invent a plugin API:

  * PROMPT_COMMAND — if set, run a shell command before each interactive
    prompt; capture the first line of stdout (existing prompt buffer limit).
  * %D — PROMPT1/PROMPT2 escape for that captured line (renderer output).
  * SHELL_EXIT — last *user* command status (SQL or \!); PROMPT_COMMAND does
    not overwrite it, so themes can show the prior command’s outcome.
  * Prompt refresh — run_prompt_command() immediately before readline() in
    input.c (not rl_pre_input_hook, which left the prompt blank until a key).
  * \connect housekeeping — reset ROW_COUNT / SHELL_EXIT after successful
    \connect; optional :txid refresh when that variable already exists.

Zero behavior change unless configured
--------------------------------------
Defaults are inert:

  | Knob | Default | Effect when unset/off |
  |-------------------------|---------|------------------------------------|
  | PROMPT_COMMAND | unset | no shell, no popen |
  | %D | unused | empty substitution if referenced |
  | PROMPT_SESSION_EXPORT | off | no PG*/PSQL_* export into subprocess |
  | SHELL_EXIT | "0" | new variable only; scripts ignore it |

Non-interactive use, existing scripts, and default interactive sessions are
unchanged. No .psqlrc change ⇒ no new behavior.

Security / gating story (PROMPT_SESSION_EXPORT + shell)
-------------------------------------------------------
PROMPT_COMMAND uses popen() — the same trust model as existing %`command`
prompt escapes and \!. It only runs when the user (or their .psqlrc)
explicitly sets PROMPT_COMMAND.

PROMPT_SESSION_EXPORT is a separate, default-off gate:

  * Off (default): PROMPT_COMMAND subprocess inherits the normal process
    environment only; no extra session dump.
  * On: export current-session PG* / PSQL_* (database, user, host, port,
    PSQL_TXN, PSQL_ROW_COUNT, PSQL_SHELL_EXIT, PSQL_SUPERUSER, optional
    PSQL_TXID) so external renderers need not re-query.

Keeping export opt-in means “I only want %D from a fixed command” never
leaks connection metadata into the child env. Reviewers who want a
narrower first commit can take PROMPT_COMMAND + %D + SHELL_EXIT alone and
leave PROMPT_SESSION_EXPORT for a follow-up.

========================================================================
Looking for a reviewer to walk this path
========================================================================

Please walk the psql + readline path (not a general “please review”):

  1. src/bin/psql/input.c
     — run_prompt_command() immediately before readline() / gets_fromFile
  2. src/bin/psql/prompt.c + prompt.h
     — run_prompt_command(), export_prompt_environment(),
       prompt_session_export_enabled(), %D in get_prompt(),
       reset_prompt_status_after_connect()
  3. src/bin/psql/mainloop.c
     — gets_interactive() call sites (when prompts are drawn)
  4. src/bin/psql/common.c
     — SetLastExitVariable() / SHELL_EXIT; confirm PROMPT_COMMAND does not
       call it
  5. src/bin/psql/command.c
     — reset_prompt_status_after_connect() after successful \connect
  6. src/bin/psql/help.c + startup.c
     — --help=variables / initial SHELL_EXIT
  7. src/bin/psql/t/040_prompt_command.pl
     — TAP coverage (below)

Optional companion doc (not installed): src/bin/psql/powerline-integration.md

========================================================================
Testing (v2: TAP added)
========================================================================

src/bin/psql/t/040_prompt_command.pl (registered in meson.build):

  Non-interactive:
    * SHELL_EXIT after successful SQL, failed SQL, \! true, \! false
    * \connect clears ROW_COUNT and SHELL_EXIT
    * --help=variables lists PROMPT_COMMAND, PROMPT_SESSION_EXPORT, SHELL_EXIT

  Interactive (IO::Pty + readline, same pattern as t/010 / t/030):
    * PROMPT_COMMAND + %D in PROMPT1
    * first line only of multi-line PROMPT_COMMAND stdout
    * %D empty when PROMPT_COMMAND unset
    * PROMPT_SESSION_EXPORT off → PSQL_TXN / PSQL_SUPERUSER not exported
    * PROMPT_SESSION_EXPORT on → PSQL_TXN / PSQL_SUPERUSER exported
    * PROMPT_COMMAND does not overwrite SHELL_EXIT after a failed SQL

Manual still useful for visual / powerline end-to-end; automation no longer
depends on it for the core knobs.

========================================================================
v2 patch notes
========================================================================

Relative to the original submission:

  * Added src/bin/psql/t/040_prompt_command.pl
  * Listed that test in src/bin/psql/meson.build
  * No intentional functional change to the feature code

Happy to rename variables, drop PROMPT_SESSION_EXPORT from v1, or further
split if that helps commit.

Companion (not part of this patch):

Fork branch:

Comments welcome.

Regards,
Kirk Roybal

Attachment

pgsql-hackers by date:

Previous
From: Gleb Kashkin
Date:
Subject: Re: Bug in asynchronous Append
Next
From: Bharath Rupireddy
Date:
Subject: Re: enhance wraparound warnings