psql: Add pipeline status to prompt and some state variables

This commit adds %P to psql prompts, able to report the status of a
pipeline depending on PQpipelineStatus(): on, off or abort.

The following variables are added to report the state of an ongoing
pipeline:
- PIPELINE_SYNC_COUNT: reports the number of piped syncs.
- PIPELINE_COMMAND_COUNT: reports the number of piped commands, a
command being either \bind, \bind_named, \close or \parse.
- PIPELINE_RESULT_COUNT: reports the results available to read with
\getresults.

These variables can be used with \echo or in a prompt, using "%:name:"
in PROMPT1, PROMPT2 or PROMPT3.  Some basic regression tests are added
for these.  The suggestion to use variables to show the details about
the status counters comes from me.  The original patch proposed was less
extensible, hardcoding the output in the prompt.

Author: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Discussion: https://postgr.es/m/CAO6_XqroE7JuMEm1sWz55rp9fAYX2JwmcP_3m_v51vnOFdsLiQ@mail.gmail.com
This commit is contained in:
Michael Paquier 2025-02-25 10:07:24 +09:00
parent cbb9086c9e
commit 3ce357584e
6 changed files with 128 additions and 1 deletions

View File

@ -3728,6 +3728,12 @@ testdb=&gt; <userinput>\setenv LESS -imx4F</userinput>
generate one result to get.
</para>
<para>
When pipeline mode is active, a dedicated prompt variable is available
to report the pipeline status.
See <xref linkend="app-psql-prompting-p-uc"/> for more details
</para>
<para>
Example:
<programlisting>
@ -4502,6 +4508,39 @@ bar
</listitem>
</varlistentry>
<varlistentry id="app-psql-variables-pipeline-command-count">
<term><varname>PIPELINE_COMMAND_COUNT</varname></term>
<listitem>
<para>
The number of commands generated by <literal>\bind</literal>,
<literal>\bind_named</literal>, <literal>\close</literal> or
<literal>\parse</literal> queued in an ongoing pipeline.
</para>
</listitem>
</varlistentry>
<varlistentry id="app-psql-variables-pipeline-result-count">
<term><varname>PIPELINE_RESULT_COUNT</varname></term>
<listitem>
<para>
The number of commands of an ongoing pipeline that were followed
by either a <command>\flushrequest</command> or a
<command>\syncpipeline</command>, forcing the server to send the
results. These results can be retrieved with
<command>\getresults</command>.
</para>
</listitem>
</varlistentry>
<varlistentry id="app-psql-variables-pipeline-sync-count">
<term><varname>PIPELINE_SYNC_COUNT</varname></term>
<listitem>
<para>
The number of sync messages queued in an ongoing pipeline.
</para>
</listitem>
</varlistentry>
<varlistentry id="app-psql-variables-port">
<term><varname>PORT</varname></term>
<listitem>
@ -4901,6 +4940,17 @@ testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
</listitem>
</varlistentry>
<varlistentry id="app-psql-prompting-p-uc">
<term><literal>%P</literal></term>
<listitem>
<para>
Pipeline status: <literal>off</literal> when not in a pipeline,
<literal>on</literal> when in an ongoing pipeline or
<literal>abort</literal> when in an aborted pipeline.
</para>
</listitem>
</varlistentry>
<varlistentry id="app-psql-prompting-r">
<term><literal>%R</literal></term>
<listitem>

View File

@ -524,6 +524,26 @@ SetShellResultVariables(int wait_result)
}
/*
* Set special pipeline variables
* - PIPELINE_SYNC_COUNT: The number of piped syncs
* - PIPELINE_COMMAND_COUNT: The number of piped commands
* - PIPELINE_RESULT_COUNT: The number of results available to read
*/
static void
SetPipelineVariables(void)
{
char buf[32];
snprintf(buf, sizeof(buf), "%d", pset.piped_syncs);
SetVariable(pset.vars, "PIPELINE_SYNC_COUNT", buf);
snprintf(buf, sizeof(buf), "%d", pset.piped_commands);
SetVariable(pset.vars, "PIPELINE_COMMAND_COUNT", buf);
snprintf(buf, sizeof(buf), "%d", pset.available_results);
SetVariable(pset.vars, "PIPELINE_RESULT_COUNT", buf);
}
/*
* ClearOrSaveResult
*
@ -1661,6 +1681,8 @@ ExecQueryAndProcessResults(const char *query,
CheckConnection();
SetPipelineVariables();
return -1;
}
@ -1669,8 +1691,10 @@ ExecQueryAndProcessResults(const char *query,
{
/*
* We are in a pipeline and have not reached the pipeline end, or
* there was no request to read pipeline results, exit.
* there was no request to read pipeline results. Update the psql
* variables tracking the pipeline activity and exit.
*/
SetPipelineVariables();
return 1;
}
@ -2105,6 +2129,7 @@ ExecQueryAndProcessResults(const char *query,
Assert(pset.available_results == 0);
}
Assert(pset.requested_results == 0);
SetPipelineVariables();
/* may need this to recover from conn loss during COPY */
if (!CheckConnection())

View File

@ -31,6 +31,7 @@
* sockets, "[local:/dir/name]" if not default
* %m - like %M, but hostname only (before first dot), or always "[local]"
* %p - backend pid
* %P - pipeline status: on, off or abort
* %> - database server port number
* %n - database user name
* %s - service
@ -181,6 +182,19 @@ get_prompt(promptStatus_t status, ConditionalStack cstack)
snprintf(buf, sizeof(buf), "%d", pid);
}
break;
/* pipeline status */
case 'P':
{
PGpipelineStatus status = PQpipelineStatus(pset.db);
if (status == PQ_PIPELINE_ON)
strlcpy(buf, "on", sizeof(buf));
else if (status == PQ_PIPELINE_ABORTED)
strlcpy(buf, "abort", sizeof(buf));
else
strlcpy(buf, "off", sizeof(buf));
break;
}
case '0':
case '1':

View File

@ -205,6 +205,11 @@ main(int argc, char *argv[])
SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3);
SetVariableBool(pset.vars, "SHOW_ALL_RESULTS");
/* Initialize pipeline variables */
SetVariable(pset.vars, "PIPELINE_SYNC_COUNT", "0");
SetVariable(pset.vars, "PIPELINE_COMMAND_COUNT", "0");
SetVariable(pset.vars, "PIPELINE_RESULT_COUNT", "0");
parse_psql_options(argc, argv, &options);
/*

View File

@ -57,12 +57,24 @@ SELECT $1, $2 \bind 'val2' 'val3' \g
-- Send multiple syncs
\startpipeline
\echo :PIPELINE_COMMAND_COUNT
0
\echo :PIPELINE_SYNC_COUNT
0
\echo :PIPELINE_RESULT_COUNT
0
SELECT $1 \bind 'val1' \g
\syncpipeline
\syncpipeline
SELECT $1, $2 \bind 'val2' 'val3' \g
\syncpipeline
SELECT $1, $2 \bind 'val4' 'val5' \g
\echo :PIPELINE_COMMAND_COUNT
1
\echo :PIPELINE_SYNC_COUNT
3
\echo :PIPELINE_RESULT_COUNT
2
\endpipeline
?column?
----------
@ -303,13 +315,21 @@ SELECT $1 \bind 2 \g
SELECT $1 \bind 1 \g
SELECT $1 \bind 2 \g
SELECT $1 \bind 3 \g
\echo :PIPELINE_SYNC_COUNT
0
\syncpipeline
\echo :PIPELINE_SYNC_COUNT
1
\echo :PIPELINE_RESULT_COUNT
3
\getresults 1
?column?
----------
1
(1 row)
\echo :PIPELINE_RESULT_COUNT
2
SELECT $1 \bind 4 \g
\getresults 3
?column?
@ -322,6 +342,8 @@ SELECT $1 \bind 4 \g
3
(1 row)
\echo :PIPELINE_RESULT_COUNT
0
\endpipeline
?column?
----------

View File

@ -27,12 +27,18 @@ SELECT $1, $2 \bind 'val2' 'val3' \g
-- Send multiple syncs
\startpipeline
\echo :PIPELINE_COMMAND_COUNT
\echo :PIPELINE_SYNC_COUNT
\echo :PIPELINE_RESULT_COUNT
SELECT $1 \bind 'val1' \g
\syncpipeline
\syncpipeline
SELECT $1, $2 \bind 'val2' 'val3' \g
\syncpipeline
SELECT $1, $2 \bind 'val4' 'val5' \g
\echo :PIPELINE_COMMAND_COUNT
\echo :PIPELINE_SYNC_COUNT
\echo :PIPELINE_RESULT_COUNT
\endpipeline
-- \startpipeline should not have any effect if already in a pipeline.
@ -174,10 +180,15 @@ SELECT $1 \bind 2 \g
SELECT $1 \bind 1 \g
SELECT $1 \bind 2 \g
SELECT $1 \bind 3 \g
\echo :PIPELINE_SYNC_COUNT
\syncpipeline
\echo :PIPELINE_SYNC_COUNT
\echo :PIPELINE_RESULT_COUNT
\getresults 1
\echo :PIPELINE_RESULT_COUNT
SELECT $1 \bind 4 \g
\getresults 3
\echo :PIPELINE_RESULT_COUNT
\endpipeline
-- \syncpipeline count as one command to fetch for \getresults.