Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * cmdtag.c
4 : * Data and routines for commandtag names and enumeration.
5 : *
6 : * Portions Copyright (c) 1996-2023, 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 "miscadmin.h"
17 : #include "tcop/cmdtag.h"
18 : #include "utils/builtins.h"
19 :
20 :
21 : typedef struct CommandTagBehavior
22 : {
23 : const char *name; /* tag name, e.g. "SELECT" */
24 : const uint8 namelen; /* set to strlen(name) */
25 : const bool event_trigger_ok;
26 : const bool table_rewrite_ok;
27 : const bool display_rowcount; /* should the number of rows affected be
28 : * shown in the command completion string */
29 : } CommandTagBehavior;
30 :
31 : #define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt) \
32 : { name, (uint8) (sizeof(name) - 1), evtrgok, rwrok, rowcnt },
33 :
34 : static const CommandTagBehavior tag_behavior[COMMAND_TAG_NEXTTAG] = {
35 : #include "tcop/cmdtaglist.h"
36 : };
37 :
38 : #undef PG_CMDTAG
39 :
40 : void
1133 alvherre 41 GIC 516242 : InitializeQueryCompletion(QueryCompletion *qc)
42 : {
43 516242 : qc->commandTag = CMDTAG_UNKNOWN;
1133 alvherre 44 CBC 516242 : qc->nprocessed = 0;
1133 alvherre 45 GIC 516242 : }
1133 alvherre 46 ECB :
47 : const char *
1133 alvherre 48 CBC 5768 : GetCommandTagName(CommandTag commandTag)
49 : {
1133 alvherre 50 GIC 5768 : return tag_behavior[commandTag].name;
1133 alvherre 51 ECB : }
52 :
53 : const char *
114 drowley 54 GNC 729542 : GetCommandTagNameAndLen(CommandTag commandTag, Size *len)
55 : {
56 729542 : *len = (Size) tag_behavior[commandTag].namelen;
57 729542 : return tag_behavior[commandTag].name;
58 : }
59 :
1133 alvherre 60 ECB : bool
1133 alvherre 61 GIC 237052 : command_tag_display_rowcount(CommandTag commandTag)
62 : {
63 237052 : return tag_behavior[commandTag].display_rowcount;
1133 alvherre 64 ECB : }
65 :
66 : bool
1133 alvherre 67 CBC 61274 : command_tag_event_trigger_ok(CommandTag commandTag)
68 : {
1133 alvherre 69 GIC 61274 : return tag_behavior[commandTag].event_trigger_ok;
70 : }
1133 alvherre 71 ECB :
72 : bool
1133 alvherre 73 CBC 41 : command_tag_table_rewrite_ok(CommandTag commandTag)
74 : {
1133 alvherre 75 GIC 41 : return tag_behavior[commandTag].table_rewrite_ok;
76 : }
1133 alvherre 77 ECB :
78 : /*
79 : * Search CommandTag by name
80 : *
81 : * Returns CommandTag, or CMDTAG_UNKNOWN if not recognized
82 : */
83 : CommandTag
1133 alvherre 84 GIC 156 : GetCommandTagEnum(const char *commandname)
1133 alvherre 85 ECB : {
86 : const CommandTagBehavior *base,
87 : *last,
88 : *position;
89 : int result;
90 :
1133 alvherre 91 GIC 156 : if (commandname == NULL || *commandname == '\0')
1133 alvherre 92 UIC 0 : return CMDTAG_UNKNOWN;
93 :
1133 alvherre 94 CBC 156 : base = tag_behavior;
1133 alvherre 95 GIC 156 : last = tag_behavior + lengthof(tag_behavior) - 1;
96 1176 : while (last >= base)
97 : {
98 1170 : position = base + ((last - base) >> 1);
99 1170 : result = pg_strcasecmp(commandname, position->name);
100 1170 : if (result == 0)
1133 alvherre 101 CBC 150 : return (CommandTag) (position - tag_behavior);
1133 alvherre 102 GBC 1020 : else if (result < 0)
1133 alvherre 103 GIC 345 : last = position - 1;
1133 alvherre 104 ECB : else
1133 alvherre 105 CBC 675 : base = position + 1;
1133 alvherre 106 ECB : }
1133 alvherre 107 GIC 6 : return CMDTAG_UNKNOWN;
1133 alvherre 108 ECB : }
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
114 drowley 122 GNC 237052 : BuildQueryCompletionString(char *buff, const QueryCompletion *qc,
123 : bool nameonly)
124 : {
125 237052 : CommandTag tag = qc->commandTag;
126 : Size taglen;
127 237052 : 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 237052 : memcpy(buff, tagname, taglen);
135 237052 : bufp = buff + taglen;
136 :
137 : /* ensure that the tagname isn't long enough to overrun the buffer */
138 237052 : 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 237052 : if (command_tag_display_rowcount(tag) && !nameonly)
148 : {
149 140906 : if (tag == CMDTAG_INSERT)
150 : {
151 24522 : *bufp++ = ' ';
152 24522 : *bufp++ = '0';
153 : }
154 140906 : *bufp++ = ' ';
155 140906 : bufp += pg_ulltoa_n(qc->nprocessed, bufp);
156 : }
157 :
158 : /* and finally, NUL terminate the string */
159 237052 : *bufp = '\0';
160 :
161 237052 : Assert((bufp - buff) == strlen(buff));
162 :
163 237052 : return bufp - buff;
164 : }
|