Skip to content

Commit ac99802

Browse files
committed
Speed up creation of command completion tags
The building of command completion tags could often be seen showing up in profiles when running high tps workloads. The query completion tags were being built with snprintf, which is slow at the best of times when compared with more manual ways of formatting strings. Here we introduce BuildQueryCompletionString() to do this job for us. We also now store the completion tag's strlen in the CommandTagBehavior struct so that we can quickly memcpy this number of bytes into the completion tag string. Appending the rows affected is done via pg_ulltoa_n. BuildQueryCompletionString returns the length of the built string. This saves us having to call strlen to figure out how many bytes to pass to pq_putmessage(). Author: David Rowley, Andres Freund Reviewed-by: Andres Freund Discussion: https://postgr.es/m/CAHoyFK-Xwqc-iY52shj0G+8K9FJpse+FuZ36XBKy78wDVnd=Qg@mail.gmail.com
1 parent d35a1af commit ac99802

File tree

4 files changed

+78
-30
lines changed

4 files changed

+78
-30
lines changed

src/backend/tcop/cmdtag.c

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,21 @@
1515

1616
#include "miscadmin.h"
1717
#include "tcop/cmdtag.h"
18+
#include "utils/builtins.h"
1819

1920

2021
typedef struct CommandTagBehavior
2122
{
22-
const char *name;
23+
const char *name; /* tag name, e.g. "SELECT" */
24+
const uint8 namelen; /* set to strlen(name) */
2325
const bool event_trigger_ok;
2426
const bool table_rewrite_ok;
25-
const bool display_rowcount;
27+
const bool display_rowcount; /* should the number of rows affected be
28+
* shown in the command completion string */
2629
} CommandTagBehavior;
2730

2831
#define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt) \
29-
{ name, evtrgok, rwrok, rowcnt },
32+
{ name, (uint8) (sizeof(name) - 1), evtrgok, rwrok, rowcnt },
3033

3134
static const CommandTagBehavior tag_behavior[COMMAND_TAG_NEXTTAG] = {
3235
#include "tcop/cmdtaglist.h"
@@ -47,6 +50,13 @@ GetCommandTagName(CommandTag commandTag)
4750
return tag_behavior[commandTag].name;
4851
}
4952

53+
const char *
54+
GetCommandTagNameAndLen(CommandTag commandTag, Size *len)
55+
{
56+
*len = (Size) tag_behavior[commandTag].namelen;
57+
return tag_behavior[commandTag].name;
58+
}
59+
5060
bool
5161
command_tag_display_rowcount(CommandTag commandTag)
5262
{
@@ -96,3 +106,59 @@ GetCommandTagEnum(const char *commandname)
96106
}
97107
return CMDTAG_UNKNOWN;
98108
}
109+
110+
/*
111+
* BuildQueryCompletionString
112+
* Build a string containing the command tag name with the
113+
* QueryCompletion's nprocessed for command tags with display_rowcount
114+
* set. Returns the strlen of the constructed string.
115+
*
116+
* The caller must ensure that buff is at least COMPLETION_TAG_BUFSIZE bytes.
117+
*
118+
* If nameonly is true, then the constructed string will contain only the tag
119+
* name.
120+
*/
121+
Size
122+
BuildQueryCompletionString(char *buff, const QueryCompletion *qc,
123+
bool nameonly)
124+
{
125+
CommandTag tag = qc->commandTag;
126+
Size taglen;
127+
const char *tagname = GetCommandTagNameAndLen(tag, &taglen);
128+
char *bufp;
129+
130+
/*
131+
* We assume the tagname is plain ASCII and therefore requires no encoding
132+
* conversion.
133+
*/
134+
memcpy(buff, tagname, taglen);
135+
bufp = buff + taglen;
136+
137+
/* ensure that the tagname isn't long enough to overrun the buffer */
138+
Assert(taglen <= COMPLETION_TAG_BUFSIZE - MAXINT8LEN - 4);
139+
140+
/*
141+
* In PostgreSQL versions 11 and earlier, it was possible to create a
142+
* table WITH OIDS. When inserting into such a table, INSERT used to
143+
* include the Oid of the inserted record in the completion tag. To
144+
* maintain compatibility in the wire protocol, we now write a "0" (for
145+
* InvalidOid) in the location where we once wrote the new record's Oid.
146+
*/
147+
if (command_tag_display_rowcount(tag) && !nameonly)
148+
{
149+
if (tag == CMDTAG_INSERT)
150+
{
151+
*bufp++ = ' ';
152+
*bufp++ = '0';
153+
}
154+
*bufp++ = ' ';
155+
bufp += pg_ulltoa_n(qc->nprocessed, bufp);
156+
}
157+
158+
/* and finally, NUL terminate the string */
159+
*bufp = '\0';
160+
161+
Assert((bufp - buff) == strlen(buff));
162+
163+
return bufp - buff;
164+
}

src/backend/tcop/dest.c

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -166,38 +166,17 @@ void
166166
EndCommand(const QueryCompletion *qc, CommandDest dest, bool force_undecorated_output)
167167
{
168168
char completionTag[COMPLETION_TAG_BUFSIZE];
169-
CommandTag tag;
170-
const char *tagname;
169+
Size len;
171170

172171
switch (dest)
173172
{
174173
case DestRemote:
175174
case DestRemoteExecute:
176175
case DestRemoteSimple:
177176

178-
/*
179-
* We assume the tagname is plain ASCII and therefore requires no
180-
* encoding conversion.
181-
*/
182-
tag = qc->commandTag;
183-
tagname = GetCommandTagName(tag);
184-
185-
/*
186-
* In PostgreSQL versions 11 and earlier, it was possible to
187-
* create a table WITH OIDS. When inserting into such a table,
188-
* INSERT used to include the Oid of the inserted record in the
189-
* completion tag. To maintain compatibility in the wire
190-
* protocol, we now write a "0" (for InvalidOid) in the location
191-
* where we once wrote the new record's Oid.
192-
*/
193-
if (command_tag_display_rowcount(tag) && !force_undecorated_output)
194-
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
195-
tag == CMDTAG_INSERT ?
196-
"%s 0 " UINT64_FORMAT : "%s " UINT64_FORMAT,
197-
tagname, qc->nprocessed);
198-
else
199-
snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s", tagname);
200-
pq_putmessage('C', completionTag, strlen(completionTag) + 1);
177+
len = BuildQueryCompletionString(completionTag, qc,
178+
force_undecorated_output);
179+
pq_putmessage('C', completionTag, len + 1);
201180

202181
case DestNone:
203182
case DestDebug:

src/include/tcop/cmdtag.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#ifndef CMDTAG_H
1414
#define CMDTAG_H
1515

16+
/* buffer size required for command completion tags */
17+
#define COMPLETION_TAG_BUFSIZE 64
1618

1719
#define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt) \
1820
tag,
@@ -50,9 +52,12 @@ CopyQueryCompletion(QueryCompletion *dst, const QueryCompletion *src)
5052

5153
extern void InitializeQueryCompletion(QueryCompletion *qc);
5254
extern const char *GetCommandTagName(CommandTag commandTag);
55+
extern const char *GetCommandTagNameAndLen(CommandTag commandTag, Size *len);
5356
extern bool command_tag_display_rowcount(CommandTag commandTag);
5457
extern bool command_tag_event_trigger_ok(CommandTag commandTag);
5558
extern bool command_tag_table_rewrite_ok(CommandTag commandTag);
5659
extern CommandTag GetCommandTagEnum(const char *commandname);
60+
extern Size BuildQueryCompletionString(char *buff, const QueryCompletion *qc,
61+
bool nameonly);
5762

5863
#endif /* CMDTAG_H */

src/include/tcop/dest.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@
7171
#include "tcop/cmdtag.h"
7272

7373

74-
/* buffer size to use for command completion tags */
75-
#define COMPLETION_TAG_BUFSIZE 64
7674

7775

7876
/* ----------------

0 commit comments

Comments
 (0)