Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * cmdtag.c
4 : * Data and routines for commandtag names and enumeration.
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * IDENTIFICATION
10 : * src/backend/tcop/cmdtag.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "tcop/cmdtag.h"
17 : #include "utils/builtins.h"
18 :
19 :
20 : typedef struct CommandTagBehavior
21 : {
22 : const char *name; /* tag name, e.g. "SELECT" */
23 : const uint8 namelen; /* set to strlen(name) */
24 : const bool event_trigger_ok;
25 : const bool table_rewrite_ok;
26 : const bool display_rowcount; /* should the number of rows affected be
27 : * shown in the command completion string */
28 : } CommandTagBehavior;
29 :
30 : #define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt) \
31 : { name, (uint8) (sizeof(name) - 1), evtrgok, rwrok, rowcnt },
32 :
33 : static const CommandTagBehavior tag_behavior[] = {
34 : #include "tcop/cmdtaglist.h"
35 : };
36 :
37 : #undef PG_CMDTAG
38 :
39 : void
40 424391 : InitializeQueryCompletion(QueryCompletion *qc)
41 : {
42 424391 : qc->commandTag = CMDTAG_UNKNOWN;
43 424391 : qc->nprocessed = 0;
44 424391 : }
45 :
46 : const char *
47 9407 : GetCommandTagName(CommandTag commandTag)
48 : {
49 9407 : return tag_behavior[commandTag].name;
50 : }
51 :
52 : const char *
53 703878 : GetCommandTagNameAndLen(CommandTag commandTag, Size *len)
54 : {
55 703878 : *len = (Size) tag_behavior[commandTag].namelen;
56 703878 : return tag_behavior[commandTag].name;
57 : }
58 :
59 : bool
60 320563 : command_tag_display_rowcount(CommandTag commandTag)
61 : {
62 320563 : return tag_behavior[commandTag].display_rowcount;
63 : }
64 :
65 : bool
66 70 : command_tag_event_trigger_ok(CommandTag commandTag)
67 : {
68 70 : return tag_behavior[commandTag].event_trigger_ok;
69 : }
70 :
71 : bool
72 0 : command_tag_table_rewrite_ok(CommandTag commandTag)
73 : {
74 0 : return tag_behavior[commandTag].table_rewrite_ok;
75 : }
76 :
77 : /*
78 : * Search CommandTag by name
79 : *
80 : * Returns CommandTag, or CMDTAG_UNKNOWN if not recognized
81 : */
82 : CommandTag
83 220 : GetCommandTagEnum(const char *commandname)
84 : {
85 : const CommandTagBehavior *base,
86 : *last,
87 : *position;
88 : int result;
89 :
90 220 : if (commandname == NULL || *commandname == '\0')
91 0 : return CMDTAG_UNKNOWN;
92 :
93 220 : base = tag_behavior;
94 220 : last = tag_behavior + lengthof(tag_behavior) - 1;
95 1570 : while (last >= base)
96 : {
97 1564 : position = base + ((last - base) >> 1);
98 1564 : result = pg_strcasecmp(commandname, position->name);
99 1564 : if (result == 0)
100 214 : return (CommandTag) (position - tag_behavior);
101 1350 : else if (result < 0)
102 503 : last = position - 1;
103 : else
104 847 : base = position + 1;
105 : }
106 6 : return CMDTAG_UNKNOWN;
107 : }
108 :
109 : /*
110 : * BuildQueryCompletionString
111 : * Build a string containing the command tag name with the
112 : * QueryCompletion's nprocessed for command tags with display_rowcount
113 : * set. Returns the strlen of the constructed string.
114 : *
115 : * The caller must ensure that buff is at least COMPLETION_TAG_BUFSIZE bytes.
116 : *
117 : * If nameonly is true, then the constructed string will contain only the tag
118 : * name.
119 : */
120 : Size
121 320563 : BuildQueryCompletionString(char *buff, const QueryCompletion *qc,
122 : bool nameonly)
123 : {
124 320563 : CommandTag tag = qc->commandTag;
125 : Size taglen;
126 320563 : const char *tagname = GetCommandTagNameAndLen(tag, &taglen);
127 : char *bufp;
128 :
129 : /*
130 : * We assume the tagname is plain ASCII and therefore requires no encoding
131 : * conversion.
132 : */
133 320563 : memcpy(buff, tagname, taglen);
134 320563 : bufp = buff + taglen;
135 :
136 : /* ensure that the tagname isn't long enough to overrun the buffer */
137 : Assert(taglen <= COMPLETION_TAG_BUFSIZE - MAXINT8LEN - 4);
138 :
139 : /*
140 : * In PostgreSQL versions 11 and earlier, it was possible to create a
141 : * table WITH OIDS. When inserting into such a table, INSERT used to
142 : * include the Oid of the inserted record in the completion tag. To
143 : * maintain compatibility in the wire protocol, we now write a "0" (for
144 : * InvalidOid) in the location where we once wrote the new record's Oid.
145 : */
146 320563 : if (command_tag_display_rowcount(tag) && !nameonly)
147 : {
148 193323 : if (tag == CMDTAG_INSERT)
149 : {
150 33392 : *bufp++ = ' ';
151 33392 : *bufp++ = '0';
152 : }
153 193323 : *bufp++ = ' ';
154 193323 : bufp += pg_ulltoa_n(qc->nprocessed, bufp);
155 : }
156 :
157 : /* and finally, NUL terminate the string */
158 320563 : *bufp = '\0';
159 :
160 : Assert((bufp - buff) == strlen(buff));
161 :
162 320563 : return bufp - buff;
163 : }
|