Portal->commandTag as an enum
Hackers,
I have implemented $subject, attached.
While reviewing the "New SQL counter statistics view (pg_stat_sql)” thread [1]/messages/by-id/CAJrrPGeY4xujjoR=z=KoyRMHEK_pSjjp=7VBhOAHq9rfgpV7QQ@mail.gmail.com, I came across Andres’ comment
That's not really something in this patch, but a lot of this would be
better if we didn't internally have command tags as strings, but as an
enum. We should really only convert to a string with needed. That
we're doing string comparisons on Portal->commandTag is just plain bad.If so, we could also make this whole patch a lot cheaper - have a fixed
size array that has an entry for every possible tag (possibly even in
shared memory, and then use atomics there).
I put the CommandTag enum in src/common because there wasn’t any reason not to do so. It seems plausible that frontend test frameworks might want access to this enum. I don’t have any frontend code using it yet, nor any concrete plans for that. I’m indifferent about this, and will move it into src/backend if folks think that’s better.
In commands/event_trigger.c, I changed the separation between EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED and EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED. It used to claim not to recognize command tags that are indeed recognized elsewhere in the system, but simply not expected here. It now returns “not supported” for them, and only returns “not recognized” for special enum values COMMANDTAG_NULL and COMMANDTAG_UNKNOWN, as well as values outside the recognized range of the enum. I’m happy to change my implementation to preserve the old behavior if necessary. Is there a backward compatibility issue here? It does not impact regression test output for me to change this, but that’s not definitive….
I have extended the event_trigger.sql regression test, with new expected output, and when applying that change to master, the test fails due to the “not supported” vs. “not recognized” distinction. I have kept this regression test change in its own patch file, 0002. The differences when applied to master look like:
create event trigger regress_event_trigger_ALTER_SYSTEM on ddl_command_start when tag in ('ALTER SYSTEM') execute procedure test_event_trigger2(); -ERROR: event triggers are not supported for ALTER SYSTEM +ERROR: filter value "ALTER SYSTEM" not recognized for filter variable "tag"
PreventCommandIfReadOnly and PreventCommandIfParallelMode sometimes take a commandTag, but in commands/sequence.c they take strings “nextval()” and “setval()”. Likewise, PreventCommandDuringRecovery takes "txid_current()” in adt/txid.c. I had to work around this a little, which was not hard to do, but it made me wonder if command tags and these sorts of functions shouldn’t be unified a bit more. They don’t really look consistent with all the other values in the CommandTag enum, so I left them out. I’m open to opinions about this.
There was some confusion in the code between a commandTag and a completionTag, with the commandTag getting overwritten with the completionTag over the course of execution. I’ve split that out into two distinctly separate concepts, which I think makes the code easier to grok. I’ve added a portal->completionTag field that is a fixed size buffer rather than a palloc’d string, to match how completionTag works elsewhere. But the old code that was overwriting the commandTag (a palloc’d string) with a completionTag (a char[] buffer) was using pstrdup for that purpose. I’m now using strlcpy. I don’t care much which way to go here (buffer vs. palloc’d string). Let me know if using a fixed sized buffer as I’ve done bothers anybody.
There were some instances of things like:
strcpy(completionTag, portal->commandTag);
which should have more properly been
strlcpy(completionTag, portal->commandTag, COMPLETION_TAG_BUFSIZE);
I don’t know if any of these were live bugs, but they seemed like traps for the future, should any new commandTag length overflow the buffer size. I think this patch fixes all of those cases.
Generating CommandTag enum values from user queries and then converting those back to string for printing or use in set_ps_display results in normalization of the commandTag, by which I mean that it becomes all uppercase. I don’t know of any situations where this would matter, but I can’t say for sure that it doesn’t. Anybody have thoughts on that?
[1]: /messages/by-id/CAJrrPGeY4xujjoR=z=KoyRMHEK_pSjjp=7VBhOAHq9rfgpV7QQ@mail.gmail.com
Attachments:
v1-0001-Migrating-commandTag-from-string-to-enum.patchapplication/octet-stream; name=v1-0001-Migrating-commandTag-from-string-to-enum.patch; x-unix-mode=0644Download
From a6e2e6493d13e14e0d52473e9af6c6b6f7947be3 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Sun, 2 Feb 2020 10:14:26 -0800
Subject: [PATCH v1 1/2] Migrating commandTag from string to enum.
The backend uses strings to represent command tags and does string
comparisons in multiple places. Fixing that by creating a new
CommandTag enum and using it instead.
---
src/backend/commands/async.c | 2 +-
src/backend/commands/copy.c | 2 +-
src/backend/commands/event_trigger.c | 383 ++++++++----
src/backend/commands/portalcmds.c | 3 +-
src/backend/commands/sequence.c | 8 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/spi.c | 6 +-
src/backend/replication/walsender.c | 4 +-
src/backend/tcop/dest.c | 10 +-
src/backend/tcop/postgres.c | 12 +-
src/backend/tcop/pquery.c | 30 +-
src/backend/tcop/utility.c | 576 ++++++++++--------
src/backend/utils/adt/txid.c | 2 +-
src/backend/utils/cache/plancache.c | 4 +-
src/backend/utils/mmgr/portalmem.c | 6 +-
src/common/Makefile | 1 +
src/common/commandtag.c | 382 ++++++++++++
src/include/common/commandtag.h | 254 ++++++++
src/include/miscadmin.h | 10 +-
src/include/nodes/parsenodes.h | 1 +
src/include/tcop/dest.h | 5 +-
src/include/tcop/utility.h | 2 +-
src/include/utils/plancache.h | 6 +-
src/include/utils/portal.h | 13 +-
src/pl/plpgsql/src/pl_exec.c | 7 +-
.../test_ddl_deparse/test_ddl_deparse.c | 2 +-
26 files changed, 1298 insertions(+), 437 deletions(-)
create mode 100644 src/common/commandtag.c
create mode 100644 src/include/common/commandtag.h
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 9aa2b61600..5322c14ce4 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -594,7 +594,7 @@ pg_notify(PG_FUNCTION_ARGS)
payload = text_to_cstring(PG_GETARG_TEXT_PP(1));
/* For NOTIFY as a statement, this is checked in ProcessUtility */
- PreventCommandDuringRecovery("NOTIFY");
+ PreventCommandDuringRecovery(COMMANDTAG_NOTIFY);
Async_Notify(channel, payload);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 40a8ec1abd..4828e75bd5 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1063,7 +1063,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
/* check read-only transaction and parallel mode */
if (XactReadOnly && !rel->rd_islocaltemp)
- PreventCommandIfReadOnly("COPY FROM");
+ PreventCommandIfReadOnly(COMMANDTAG_COPY_FROM);
cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 71911d4067..ad80616f78 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -32,6 +32,7 @@
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/trigger.h"
+#include "common/commandtag.h"
#include "funcapi.h"
#include "lib/ilist.h"
#include "miscadmin.h"
@@ -85,52 +86,6 @@ typedef enum
EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED
} event_trigger_command_tag_check_result;
-/* XXX merge this with ObjectTypeMap? */
-static const event_trigger_support_data event_trigger_support[] = {
- {"ACCESS METHOD", true},
- {"AGGREGATE", true},
- {"CAST", true},
- {"CONSTRAINT", true},
- {"COLLATION", true},
- {"CONVERSION", true},
- {"DATABASE", false},
- {"DOMAIN", true},
- {"EXTENSION", true},
- {"EVENT TRIGGER", false},
- {"FOREIGN DATA WRAPPER", true},
- {"FOREIGN TABLE", true},
- {"FUNCTION", true},
- {"INDEX", true},
- {"LANGUAGE", true},
- {"MATERIALIZED VIEW", true},
- {"OPERATOR", true},
- {"OPERATOR CLASS", true},
- {"OPERATOR FAMILY", true},
- {"POLICY", true},
- {"PROCEDURE", true},
- {"PUBLICATION", true},
- {"ROLE", false},
- {"ROUTINE", true},
- {"RULE", true},
- {"SCHEMA", true},
- {"SEQUENCE", true},
- {"SERVER", true},
- {"STATISTICS", true},
- {"SUBSCRIPTION", true},
- {"TABLE", true},
- {"TABLESPACE", false},
- {"TRANSFORM", true},
- {"TRIGGER", true},
- {"TEXT SEARCH CONFIGURATION", true},
- {"TEXT SEARCH DICTIONARY", true},
- {"TEXT SEARCH PARSER", true},
- {"TEXT SEARCH TEMPLATE", true},
- {"TYPE", true},
- {"USER MAPPING", true},
- {"VIEW", true},
- {NULL, false}
-};
-
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -150,8 +105,8 @@ typedef struct SQLDropObject
static void AlterEventTriggerOwner_internal(Relation rel,
HeapTuple tup,
Oid newOwnerId);
-static event_trigger_command_tag_check_result check_ddl_tag(const char *tag);
-static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(const char *tag);
+static event_trigger_command_tag_check_result check_ddl_tag(CommandTag commandTag);
+static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(CommandTag commandTag);
static void error_duplicate_filter_variable(const char *defname);
static Datum filter_list_to_array(List *filterlist);
static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname,
@@ -259,69 +214,264 @@ validate_ddl_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
event_trigger_command_tag_check_result result;
- result = check_ddl_tag(tag);
+ result = check_ddl_tag(commandTag);
if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
- tag, filtervar)));
+ tagstr, filtervar)));
if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
static event_trigger_command_tag_check_result
-check_ddl_tag(const char *tag)
+check_ddl_tag(CommandTag commandTag)
{
- const char *obtypename;
- const event_trigger_support_data *etsd;
+ switch (commandTag)
+ {
+ /*
+ * Supported idiosyncratic special cases.
+ */
+ case COMMANDTAG_ALTER_DEFAULT_PRIVILEGES:
+ case COMMANDTAG_ALTER_LARGE_OBJECT:
+ case COMMANDTAG_COMMENT:
+ case COMMANDTAG_CREATE_TABLE_AS:
+ case COMMANDTAG_DROP_OWNED:
+ case COMMANDTAG_GRANT:
+ case COMMANDTAG_IMPORT_FOREIGN_SCHEMA:
+ case COMMANDTAG_REFRESH_MATERIALIZED_VIEW:
+ case COMMANDTAG_REVOKE:
+ case COMMANDTAG_SECURITY_LABEL:
+ case COMMANDTAG_SELECT_INTO:
- /*
- * Handle some idiosyncratic special cases.
- */
- if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
- pg_strcasecmp(tag, "SELECT INTO") == 0 ||
- pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
- pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
- pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
- pg_strcasecmp(tag, "COMMENT") == 0 ||
- pg_strcasecmp(tag, "GRANT") == 0 ||
- pg_strcasecmp(tag, "REVOKE") == 0 ||
- pg_strcasecmp(tag, "DROP OWNED") == 0 ||
- pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
+ /*
+ * Supported CREATE commands
+ */
+ case COMMANDTAG_CREATE_ACCESS_METHOD:
+ case COMMANDTAG_CREATE_AGGREGATE:
+ case COMMANDTAG_CREATE_CAST:
+ case COMMANDTAG_CREATE_COLLATION:
+ case COMMANDTAG_CREATE_CONSTRAINT:
+ case COMMANDTAG_CREATE_CONVERSION:
+ case COMMANDTAG_CREATE_DOMAIN:
+ case COMMANDTAG_CREATE_EXTENSION:
+ case COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER:
+ case COMMANDTAG_CREATE_FOREIGN_TABLE:
+ case COMMANDTAG_CREATE_FUNCTION:
+ case COMMANDTAG_CREATE_INDEX:
+ case COMMANDTAG_CREATE_LANGUAGE:
+ case COMMANDTAG_CREATE_MATERIALIZED_VIEW:
+ case COMMANDTAG_CREATE_OPERATOR:
+ case COMMANDTAG_CREATE_OPERATOR_CLASS:
+ case COMMANDTAG_CREATE_OPERATOR_FAMILY:
+ case COMMANDTAG_CREATE_POLICY:
+ case COMMANDTAG_CREATE_PROCEDURE:
+ case COMMANDTAG_CREATE_PUBLICATION:
+ case COMMANDTAG_CREATE_ROUTINE:
+ case COMMANDTAG_CREATE_RULE:
+ case COMMANDTAG_CREATE_SCHEMA:
+ case COMMANDTAG_CREATE_SEQUENCE:
+ case COMMANDTAG_CREATE_SERVER:
+ case COMMANDTAG_CREATE_STATISTICS:
+ case COMMANDTAG_CREATE_SUBSCRIPTION:
+ case COMMANDTAG_CREATE_TABLE:
+ case COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION:
+ case COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY:
+ case COMMANDTAG_CREATE_TEXT_SEARCH_PARSER:
+ case COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE:
+ case COMMANDTAG_CREATE_TRANSFORM:
+ case COMMANDTAG_CREATE_TRIGGER:
+ case COMMANDTAG_CREATE_TYPE:
+ case COMMANDTAG_CREATE_USER_MAPPING:
+ case COMMANDTAG_CREATE_VIEW:
- /*
- * Otherwise, command should be CREATE, ALTER, or DROP.
- */
- if (pg_strncasecmp(tag, "CREATE ", 7) == 0)
- obtypename = tag + 7;
- else if (pg_strncasecmp(tag, "ALTER ", 6) == 0)
- obtypename = tag + 6;
- else if (pg_strncasecmp(tag, "DROP ", 5) == 0)
- obtypename = tag + 5;
- else
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
+ /*
+ * Supported ALTER commands
+ */
+ case COMMANDTAG_ALTER_ACCESS_METHOD:
+ case COMMANDTAG_ALTER_AGGREGATE:
+ case COMMANDTAG_ALTER_CAST:
+ case COMMANDTAG_ALTER_COLLATION:
+ case COMMANDTAG_ALTER_CONSTRAINT:
+ case COMMANDTAG_ALTER_CONVERSION:
+ case COMMANDTAG_ALTER_DOMAIN:
+ case COMMANDTAG_ALTER_EXTENSION:
+ case COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER:
+ case COMMANDTAG_ALTER_FOREIGN_TABLE:
+ case COMMANDTAG_ALTER_FUNCTION:
+ case COMMANDTAG_ALTER_INDEX:
+ case COMMANDTAG_ALTER_LANGUAGE:
+ case COMMANDTAG_ALTER_MATERIALIZED_VIEW:
+ case COMMANDTAG_ALTER_OPERATOR:
+ case COMMANDTAG_ALTER_OPERATOR_CLASS:
+ case COMMANDTAG_ALTER_OPERATOR_FAMILY:
+ case COMMANDTAG_ALTER_POLICY:
+ case COMMANDTAG_ALTER_PROCEDURE:
+ case COMMANDTAG_ALTER_PUBLICATION:
+ case COMMANDTAG_ALTER_ROUTINE:
+ case COMMANDTAG_ALTER_RULE:
+ case COMMANDTAG_ALTER_SCHEMA:
+ case COMMANDTAG_ALTER_SEQUENCE:
+ case COMMANDTAG_ALTER_SERVER:
+ case COMMANDTAG_ALTER_STATISTICS:
+ case COMMANDTAG_ALTER_SUBSCRIPTION:
+ case COMMANDTAG_ALTER_TABLE:
+ case COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION:
+ case COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY:
+ case COMMANDTAG_ALTER_TEXT_SEARCH_PARSER:
+ case COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE:
+ case COMMANDTAG_ALTER_TRANSFORM:
+ case COMMANDTAG_ALTER_TRIGGER:
+ case COMMANDTAG_ALTER_TYPE:
+ case COMMANDTAG_ALTER_USER_MAPPING:
+ case COMMANDTAG_ALTER_VIEW:
- /*
- * ...and the object type should be something recognizable.
- */
- for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++)
- if (pg_strcasecmp(etsd->obtypename, obtypename) == 0)
- break;
- if (etsd->obtypename == NULL)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
- if (!etsd->supported)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
- return EVENT_TRIGGER_COMMAND_TAG_OK;
+ /*
+ * Supported DROP commands
+ */
+ case COMMANDTAG_DROP_ACCESS_METHOD:
+ case COMMANDTAG_DROP_AGGREGATE:
+ case COMMANDTAG_DROP_CAST:
+ case COMMANDTAG_DROP_COLLATION:
+ case COMMANDTAG_DROP_CONSTRAINT:
+ case COMMANDTAG_DROP_CONVERSION:
+ case COMMANDTAG_DROP_DOMAIN:
+ case COMMANDTAG_DROP_EXTENSION:
+ case COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER:
+ case COMMANDTAG_DROP_FOREIGN_TABLE:
+ case COMMANDTAG_DROP_FUNCTION:
+ case COMMANDTAG_DROP_INDEX:
+ case COMMANDTAG_DROP_LANGUAGE:
+ case COMMANDTAG_DROP_MATERIALIZED_VIEW:
+ case COMMANDTAG_DROP_OPERATOR:
+ case COMMANDTAG_DROP_OPERATOR_CLASS:
+ case COMMANDTAG_DROP_OPERATOR_FAMILY:
+ case COMMANDTAG_DROP_POLICY:
+ case COMMANDTAG_DROP_PROCEDURE:
+ case COMMANDTAG_DROP_PUBLICATION:
+ case COMMANDTAG_DROP_ROUTINE:
+ case COMMANDTAG_DROP_RULE:
+ case COMMANDTAG_DROP_SCHEMA:
+ case COMMANDTAG_DROP_SEQUENCE:
+ case COMMANDTAG_DROP_SERVER:
+ case COMMANDTAG_DROP_STATISTICS:
+ case COMMANDTAG_DROP_SUBSCRIPTION:
+ case COMMANDTAG_DROP_TABLE:
+ case COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION:
+ case COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY:
+ case COMMANDTAG_DROP_TEXT_SEARCH_PARSER:
+ case COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE:
+ case COMMANDTAG_DROP_TRANSFORM:
+ case COMMANDTAG_DROP_TRIGGER:
+ case COMMANDTAG_DROP_TYPE:
+ case COMMANDTAG_DROP_USER_MAPPING:
+ case COMMANDTAG_DROP_VIEW:
+ return EVENT_TRIGGER_COMMAND_TAG_OK;
+
+ /*
+ * Unsupported CREATE commands
+ */
+ case COMMANDTAG_CREATE_DATABASE:
+ case COMMANDTAG_CREATE_EVENT_TRIGGER:
+ case COMMANDTAG_CREATE_ROLE:
+ case COMMANDTAG_CREATE_TABLESPACE:
+
+ /*
+ * Unsupported ALTER commands
+ */
+ case COMMANDTAG_ALTER_DATABASE:
+ case COMMANDTAG_ALTER_EVENT_TRIGGER:
+ case COMMANDTAG_ALTER_ROLE:
+ case COMMANDTAG_ALTER_TABLESPACE:
+
+ /*
+ * Unsupported DROP commands
+ */
+ case COMMANDTAG_DROP_DATABASE:
+ case COMMANDTAG_DROP_EVENT_TRIGGER:
+ case COMMANDTAG_DROP_ROLE:
+ case COMMANDTAG_DROP_TABLESPACE:
+
+ /*
+ * Other unsupported commands. These used to return
+ * EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED prior to the
+ * conversion of commandTag from string to enum.
+ */
+ case COMMANDTAG_ALTER_SYSTEM:
+ case COMMANDTAG_ANALYZE:
+ case COMMANDTAG_BEGIN:
+ case COMMANDTAG_CALL:
+ case COMMANDTAG_CHECKPOINT:
+ case COMMANDTAG_CLOSE:
+ case COMMANDTAG_CLOSE_CURSOR:
+ case COMMANDTAG_CLOSE_CURSOR_ALL:
+ case COMMANDTAG_CLUSTER:
+ case COMMANDTAG_COMMIT:
+ case COMMANDTAG_COMMIT_PREPARED:
+ case COMMANDTAG_COPY:
+ case COMMANDTAG_COPY_FROM:
+ case COMMANDTAG_DEALLOCATE:
+ case COMMANDTAG_DEALLOCATE_ALL:
+ case COMMANDTAG_DECLARE_CURSOR:
+ case COMMANDTAG_DELETE:
+ case COMMANDTAG_DISCARD:
+ case COMMANDTAG_DISCARD_ALL:
+ case COMMANDTAG_DISCARD_PLANS:
+ case COMMANDTAG_DISCARD_SEQUENCES:
+ case COMMANDTAG_DISCARD_TEMP:
+ case COMMANDTAG_DO:
+ case COMMANDTAG_DROP_REPLICATION_SLOT:
+ case COMMANDTAG_EXECUTE:
+ case COMMANDTAG_EXPLAIN:
+ case COMMANDTAG_FETCH:
+ case COMMANDTAG_GRANT_ROLE:
+ case COMMANDTAG_INSERT:
+ case COMMANDTAG_LISTEN:
+ case COMMANDTAG_LOAD:
+ case COMMANDTAG_LOCK_TABLE:
+ case COMMANDTAG_MOVE:
+ case COMMANDTAG_NOTIFY:
+ case COMMANDTAG_PREPARE:
+ case COMMANDTAG_PREPARE_TRANSACTION:
+ case COMMANDTAG_REASSIGN_OWNED:
+ case COMMANDTAG_REINDEX:
+ case COMMANDTAG_RELEASE:
+ case COMMANDTAG_RESET:
+ case COMMANDTAG_REVOKE_ROLE:
+ case COMMANDTAG_ROLLBACK:
+ case COMMANDTAG_ROLLBACK_PREPARED:
+ case COMMANDTAG_SAVEPOINT:
+ case COMMANDTAG_SELECT:
+ case COMMANDTAG_SELECT_FOR_KEY_SHARE:
+ case COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE:
+ case COMMANDTAG_SELECT_FOR_SHARE:
+ case COMMANDTAG_SELECT_FOR_UPDATE:
+ case COMMANDTAG_SET:
+ case COMMANDTAG_SET_CONSTRAINTS:
+ case COMMANDTAG_SHOW:
+ case COMMANDTAG_START_TRANSACTION:
+ case COMMANDTAG_TRUNCATE_TABLE:
+ case COMMANDTAG_UNLISTEN:
+ case COMMANDTAG_UPDATE:
+ case COMMANDTAG_VACUUM:
+ return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
+
+ case COMMANDTAG_NULL:
+ case COMMANDTAG_UNKNOWN:
+ break; /* fall through */
+
+ /* Intentionally no 'default' clause. */
+ }
+ return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
}
/*
@@ -334,26 +484,37 @@ validate_table_rewrite_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
event_trigger_command_tag_check_result result;
- result = check_table_rewrite_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s represents an SQL statement name */
- errmsg("event triggers are not supported for %s",
- tag)));
+ result = check_table_rewrite_ddl_tag(commandTag);
+ switch (result)
+ {
+ case EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED:
+ case EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ /* translator: %s represents an SQL statement name */
+ errmsg("event triggers are not supported for %s",
+ tagstr)));
+ case EVENT_TRIGGER_COMMAND_TAG_OK:
+ break;
+ }
}
}
static event_trigger_command_tag_check_result
-check_table_rewrite_ddl_tag(const char *tag)
+check_table_rewrite_ddl_tag(CommandTag commandTag)
{
- if (pg_strcasecmp(tag, "ALTER TABLE") == 0 ||
- pg_strcasecmp(tag, "ALTER TYPE") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
+ switch (commandTag)
+ {
+ case COMMANDTAG_ALTER_TABLE:
+ case COMMANDTAG_ALTER_TYPE:
+ return EVENT_TRIGGER_COMMAND_TAG_OK;
+ default:
+ break;
+ }
return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
}
@@ -721,7 +882,7 @@ EventTriggerCommonSetup(Node *parsetree,
*/
#ifdef USE_ASSERT_CHECKING
{
- const char *dbgtag;
+ CommandTag dbgtag;
dbgtag = CreateCommandTag(parsetree);
if (event == EVT_DDLCommandStart ||
@@ -729,12 +890,12 @@ EventTriggerCommonSetup(Node *parsetree,
event == EVT_SQLDrop)
{
if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
else if (event == EVT_TableRewrite)
{
if (check_table_rewrite_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
}
#endif
@@ -745,7 +906,7 @@ EventTriggerCommonSetup(Node *parsetree,
return NIL;
/* Get the command tag. */
- tag = CreateCommandTag(parsetree);
+ tag = GetCommandTagName(CreateCommandTag(parsetree));
/*
* Filter list of event triggers by command tag, and copy them into our
@@ -2136,7 +2297,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
values[i++] = Int32GetDatum(addr.objectSubId);
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(GetCommandTagName(CreateCommandTag(cmd->parsetree)));
/* object_type */
values[i++] = CStringGetTextDatum(type);
/* schema */
@@ -2161,7 +2322,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
nulls[i++] = true;
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(GetCommandTagName(CreateCommandTag(cmd->parsetree)));
/* object_type */
values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
/* schema */
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 7e5c805a1e..ef541e8d01 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -106,7 +106,8 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
PortalDefineQuery(portal,
NULL,
queryString,
- "SELECT", /* cursor's query is always a SELECT */
+ COMMANDTAG_SELECT, /* cursor's query is always a
+ * SELECT */
list_make1(plan),
NULL);
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 6aab73bfd4..d6c06fec9f 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -612,14 +612,14 @@ nextval_internal(Oid relid, bool check_permissions)
/* read-only transactions may only modify temp sequences */
if (!seqrel->rd_islocaltemp)
- PreventCommandIfReadOnly("nextval()");
+ PreventCommandStrIfReadOnly("nextval()");
/*
* Forbid this during parallel operation because, to make it work, the
* cooperating backends would need to share the backend-local cached
* sequence information. Currently, we don't support that.
*/
- PreventCommandIfParallelMode("nextval()");
+ PreventCommandStrIfParallelMode("nextval()");
if (elm->last != elm->cached) /* some numbers were cached */
{
@@ -937,14 +937,14 @@ do_setval(Oid relid, int64 next, bool iscalled)
/* read-only transactions may only modify temp sequences */
if (!seqrel->rd_islocaltemp)
- PreventCommandIfReadOnly("setval()");
+ PreventCommandStrIfReadOnly("setval()");
/*
* Forbid this during parallel operation because, to make it work, the
* cooperating backends would need to share the backend-local cached
* sequence information. Currently, we don't support that.
*/
- PreventCommandIfParallelMode("setval()");
+ PreventCommandStrIfParallelMode("setval()");
/* lock page' buffer and read tuple */
seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 5cff6c4321..57ae9b6c7f 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -530,7 +530,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a SQL function",
- CreateCommandTag(stmt->utilityStmt))));
+ GetCommandTagName(CreateCommandTag(stmt->utilityStmt)))));
}
if (fcache->readonly_func && !CommandIsReadOnly(stmt))
@@ -538,7 +538,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ GetCommandTagName(CreateCommandTag((Node *) stmt)))));
/* OK, build the execution_state for this query */
newes = (execution_state *) palloc(sizeof(execution_state));
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index c46764bf42..ab081eeebc 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1338,7 +1338,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
/* translator: %s is name of a SQL command, eg INSERT */
errmsg("cannot open %s query as cursor",
- plansource->commandTag)));
+ GetCommandTagName(plansource->commandTag))));
}
Assert(list_length(plan->plancache_list) == 1);
@@ -1469,7 +1469,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) pstmt))));
+ GetCommandTagName(CreateCommandTag((Node *) pstmt)))));
}
}
@@ -2255,7 +2255,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ GetCommandTagName(CreateCommandTag((Node *) stmt)))));
/*
* If not read-only mode, advance the command counter before each
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index abb533b9d0..44e4c97ebc 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1075,7 +1075,7 @@ static void
DropReplicationSlot(DropReplicationSlotCmd *cmd)
{
ReplicationSlotDrop(cmd->slotname, !cmd->wait);
- EndCommand("DROP_REPLICATION_SLOT", DestRemote);
+ EndCommand(GetCommandTagName(COMMANDTAG_DROP_REPLICATION_SLOT), DestRemote);
}
/*
@@ -1614,7 +1614,7 @@ exec_replication_command(const char *cmd_string)
MemoryContextDelete(cmd_context);
/* Send CommandComplete message */
- EndCommand("SELECT", DestRemote);
+ EndCommand(GetCommandTagName(COMMANDTAG_SELECT), DestRemote);
/* Report to pgstat that this process is now idle */
pgstat_report_activity(STATE_IDLE, NULL);
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 09c1dcbb53..5fed5b1b23 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -100,7 +100,7 @@ DestReceiver *None_Receiver = (DestReceiver *) &donothingDR;
* ----------------
*/
void
-BeginCommand(const char *commandTag, CommandDest dest)
+BeginCommand(CommandTag commandTag, CommandDest dest)
{
/* Nothing to do at present */
}
@@ -163,7 +163,7 @@ CreateDestReceiver(CommandDest dest)
* ----------------
*/
void
-EndCommand(const char *commandTag, CommandDest dest)
+EndCommand(const char *completionTag, CommandDest dest)
{
switch (dest)
{
@@ -172,10 +172,10 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestRemoteSimple:
/*
- * We assume the commandTag is plain ASCII and therefore requires
- * no encoding conversion.
+ * We assume the completionTag is plain ASCII and therefore
+ * requires no encoding conversion.
*/
- pq_putmessage('C', commandTag, strlen(commandTag) + 1);
+ pq_putmessage('C', completionTag, strlen(completionTag) + 1);
break;
case DestNone:
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0a6f80963b..8c4ee7367a 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1064,7 +1064,7 @@ exec_simple_query(const char *query_string)
{
RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);
bool snapshot_set = false;
- const char *commandTag;
+ CommandTag commandTag;
char completionTag[COMPLETION_TAG_BUFSIZE];
MemoryContext per_parsetree_context = NULL;
List *querytree_list,
@@ -1081,7 +1081,7 @@ exec_simple_query(const char *query_string)
*/
commandTag = CreateCommandTag(parsetree->stmt);
- set_ps_display(commandTag, false);
+ set_ps_display(GetCommandTagName(commandTag), false);
BeginCommand(commandTag, dest);
@@ -1343,7 +1343,7 @@ exec_parse_message(const char *query_string, /* string to execute */
MemoryContext oldcontext;
List *parsetree_list;
RawStmt *raw_parse_tree;
- const char *commandTag;
+ CommandTag commandTag;
List *querytree_list;
CachedPlanSource *psrc;
bool is_named;
@@ -1505,7 +1505,7 @@ exec_parse_message(const char *query_string, /* string to execute */
{
/* Empty input string. This is legal. */
raw_parse_tree = NULL;
- commandTag = NULL;
+ commandTag = COMMANDTAG_NULL;
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
querytree_list = NIL;
}
@@ -2049,7 +2049,7 @@ exec_execute_message(const char *portal_name, long max_rows)
* If the original query was a null string, just return
* EmptyQueryResponse.
*/
- if (portal->commandTag == NULL)
+ if (portal->commandTag == COMMANDTAG_NULL)
{
Assert(portal->stmts == NIL);
NullCommand(dest);
@@ -2095,7 +2095,7 @@ exec_execute_message(const char *portal_name, long max_rows)
pgstat_report_activity(STATE_RUNNING, sourceText);
- set_ps_display(portal->commandTag, false);
+ set_ps_display(GetCommandTagName(portal->commandTag), false);
if (save_log_statement_stats)
ResetUsage();
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 0f5801e046..8b8ac354fd 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -774,13 +774,13 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
* gave us a pointer to store it, copy it. Patch the "SELECT"
* tag to also provide the rowcount.
*/
- if (completionTag && portal->commandTag)
+ if (completionTag && portal->commandTag != COMMANDTAG_NULL)
{
- if (strcmp(portal->commandTag, "SELECT") == 0)
+ if (portal->commandTag == COMMANDTAG_SELECT)
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
"SELECT " UINT64_FORMAT, nprocessed);
else
- strcpy(completionTag, portal->commandTag);
+ strlcpy(completionTag, PortalGetCompletionTag(portal), COMPLETION_TAG_BUFSIZE);
}
/* Mark portal not active */
@@ -1044,7 +1044,17 @@ FillPortalStore(Portal portal, bool isTopLevel)
/* Override default completion tag with actual command result */
if (completionTag[0] != '\0')
- portal->commandTag = pstrdup(completionTag);
+ {
+ /*
+ * The commandTag used to serve double duty, and we overwrote it here
+ * with the completionTag. That would mean, for instance, that if the
+ * original commandTag was EXECUTE, we'd overwrite that with SELECT 5
+ * or whatever. We now store the completiionTag in this context and
+ * leave the portal's commandTag alone. If you want to extract a
+ * commandTag from the completionTag, you can always do so explicitly.
+ */
+ strlcpy(portal->completionTag, completionTag, COMPLETION_TAG_BUFSIZE);
+ }
treceiver->rDestroy(treceiver);
}
@@ -1363,16 +1373,16 @@ PortalRunMulti(Portal portal,
*/
if (completionTag && completionTag[0] == '\0')
{
- if (portal->commandTag)
- strcpy(completionTag, portal->commandTag);
+ if (portal->commandTag != COMMANDTAG_NULL)
+ strlcpy(completionTag, PortalGetCompletionTag(portal), COMPLETION_TAG_BUFSIZE);
if (strcmp(completionTag, "SELECT") == 0)
- sprintf(completionTag, "SELECT 0 0");
+ strlcpy(completionTag, "SELECT 0 0", COMPLETION_TAG_BUFSIZE);
else if (strcmp(completionTag, "INSERT") == 0)
- strcpy(completionTag, "INSERT 0 0");
+ strlcpy(completionTag, "INSERT 0 0", COMPLETION_TAG_BUFSIZE);
else if (strcmp(completionTag, "UPDATE") == 0)
- strcpy(completionTag, "UPDATE 0");
+ strlcpy(completionTag, "UPDATE 0", COMPLETION_TAG_BUFSIZE);
else if (strcmp(completionTag, "DELETE") == 0)
- strcpy(completionTag, "DELETE 0");
+ strlcpy(completionTag, "DELETE 0", COMPLETION_TAG_BUFSIZE);
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index bb85b5e52a..850e19846d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -396,20 +396,33 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree)
}
/*
- * PreventCommandIfReadOnly: throw error if XactReadOnly
+ * PreventCommandStrIfReadOnly: throw error if XactReadOnly
*
* This is useful partly to ensure consistency of the error message wording;
* some callers have checked XactReadOnly for themselves.
*/
+static inline void
+PreventCommandReadOnly(const char *commandstr)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
+ /* translator: %s is name of a SQL command, eg CREATE */
+ errmsg("cannot execute %s in a read-only transaction",
+ commandstr)));
+}
+
void
-PreventCommandIfReadOnly(const char *cmdname)
+PreventCommandStrIfReadOnly(const char *commandstr)
{
if (XactReadOnly)
- ereport(ERROR,
- (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
- /* translator: %s is name of a SQL command, eg CREATE */
- errmsg("cannot execute %s in a read-only transaction",
- cmdname)));
+ PreventCommandReadOnly(commandstr);
+}
+
+void
+PreventCommandIfReadOnly(CommandTag commandTag)
+{
+ if (XactReadOnly)
+ PreventCommandReadOnly(GetCommandTagName(commandTag));
}
/*
@@ -419,15 +432,28 @@ PreventCommandIfReadOnly(const char *cmdname)
* This is useful partly to ensure consistency of the error message wording;
* some callers have checked IsInParallelMode() for themselves.
*/
+static inline void
+PreventCommandParallelMode(const char *commandstr)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
+ /* translator: %s is name of a SQL command, eg CREATE */
+ errmsg("cannot execute %s during a parallel operation",
+ commandstr)));
+}
+
void
-PreventCommandIfParallelMode(const char *cmdname)
+PreventCommandStrIfParallelMode(const char *commandstr)
{
if (IsInParallelMode())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- /* translator: %s is name of a SQL command, eg CREATE */
- errmsg("cannot execute %s during a parallel operation",
- cmdname)));
+ PreventCommandParallelMode(commandstr);
+}
+
+void
+PreventCommandIfParallelMode(CommandTag commandTag)
+{
+ if (IsInParallelMode())
+ PreventCommandParallelMode(GetCommandTagName(commandTag));
}
/*
@@ -438,15 +464,28 @@ PreventCommandIfParallelMode(const char *cmdname)
* commands that are allowed in "read-only" xacts but cannot be allowed
* in Hot Standby mode. Those commands should call this function.
*/
+static inline void
+PreventCommandRecovery(const char *commandstr)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
+ /* translator: %s is name of a SQL command, eg CREATE */
+ errmsg("cannot execute %s during recovery",
+ commandstr)));
+}
+
void
-PreventCommandDuringRecovery(const char *cmdname)
+PreventCommandStrDuringRecovery(const char *commandstr)
{
if (RecoveryInProgress())
- ereport(ERROR,
- (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
- /* translator: %s is name of a SQL command, eg CREATE */
- errmsg("cannot execute %s during recovery",
- cmdname)));
+ PreventCommandRecovery(commandstr);
+}
+
+void
+PreventCommandDuringRecovery(CommandTag commandTag)
+{
+ if (RecoveryInProgress())
+ PreventCommandRecovery(GetCommandTagName(commandTag));
}
/*
@@ -457,14 +496,14 @@ PreventCommandDuringRecovery(const char *cmdname)
* better-defined protection mechanism, such as ownership.
*/
static void
-CheckRestrictedOperation(const char *cmdname)
+CheckRestrictedOperation(CommandTag commandTag)
{
if (InSecurityRestrictedOperation())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
/* translator: %s is name of a SQL command, eg PREPARE */
errmsg("cannot execute %s within security-restricted operation",
- cmdname)));
+ GetCommandTagName(commandTag))));
}
@@ -562,7 +601,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (readonly_flags != COMMAND_IS_STRICTLY_READ_ONLY &&
(XactReadOnly || IsInParallelMode()))
{
- const char *commandtag = CreateCommandTag(parsetree);
+ CommandTag commandtag = CreateCommandTag(parsetree);
if ((readonly_flags & COMMAND_OK_IN_READ_ONLY_TXN) == 0)
PreventCommandIfReadOnly(commandtag);
@@ -687,7 +726,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
{
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
- CheckRestrictedOperation("CLOSE");
+ CheckRestrictedOperation(COMMANDTAG_CLOSE);
PerformPortalClose(stmt->portalname);
}
break;
@@ -736,7 +775,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case T_PrepareStmt:
- CheckRestrictedOperation("PREPARE");
+ CheckRestrictedOperation(COMMANDTAG_PREPARE);
PrepareQuery(pstate, (PrepareStmt *) parsetree,
pstmt->stmt_location, pstmt->stmt_len);
break;
@@ -749,7 +788,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case T_DeallocateStmt:
- CheckRestrictedOperation("DEALLOCATE");
+ CheckRestrictedOperation(COMMANDTAG_DEALLOCATE);
DeallocateQuery((DeallocateStmt *) parsetree);
break;
@@ -793,7 +832,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
{
ListenStmt *stmt = (ListenStmt *) parsetree;
- CheckRestrictedOperation("LISTEN");
+ CheckRestrictedOperation(COMMANDTAG_LISTEN);
Async_Listen(stmt->conditionname);
}
break;
@@ -802,7 +841,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
{
UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
- CheckRestrictedOperation("UNLISTEN");
+ CheckRestrictedOperation(COMMANDTAG_UNLISTEN);
if (stmt->conditionname)
Async_Unlisten(stmt->conditionname);
else
@@ -855,7 +894,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case T_DiscardStmt:
/* should we allow DISCARD PLANS? */
- CheckRestrictedOperation("DISCARD");
+ CheckRestrictedOperation(COMMANDTAG_DISCARD);
DiscardCommand((DiscardStmt *) parsetree, isTopLevel);
break;
@@ -2099,137 +2138,137 @@ UtilityContainsQuery(Node *parsetree)
*
* This covers most cases where ALTER is used with an ObjectType enum.
*/
-static const char *
+static CommandTag
AlterObjectTypeCommandTag(ObjectType objtype)
{
- const char *tag;
+ CommandTag tag;
switch (objtype)
{
case OBJECT_AGGREGATE:
- tag = "ALTER AGGREGATE";
+ tag = COMMANDTAG_ALTER_AGGREGATE;
break;
case OBJECT_ATTRIBUTE:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case OBJECT_CAST:
- tag = "ALTER CAST";
+ tag = COMMANDTAG_ALTER_CAST;
break;
case OBJECT_COLLATION:
- tag = "ALTER COLLATION";
+ tag = COMMANDTAG_ALTER_COLLATION;
break;
case OBJECT_COLUMN:
- tag = "ALTER TABLE";
+ tag = COMMANDTAG_ALTER_TABLE;
break;
case OBJECT_CONVERSION:
- tag = "ALTER CONVERSION";
+ tag = COMMANDTAG_ALTER_CONVERSION;
break;
case OBJECT_DATABASE:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case OBJECT_DOMAIN:
case OBJECT_DOMCONSTRAINT:
- tag = "ALTER DOMAIN";
+ tag = COMMANDTAG_ALTER_DOMAIN;
break;
case OBJECT_EXTENSION:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case OBJECT_FDW:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "ALTER SERVER";
+ tag = COMMANDTAG_ALTER_SERVER;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "ALTER FOREIGN TABLE";
+ tag = COMMANDTAG_ALTER_FOREIGN_TABLE;
break;
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = COMMANDTAG_ALTER_FUNCTION;
break;
case OBJECT_INDEX:
- tag = "ALTER INDEX";
+ tag = COMMANDTAG_ALTER_INDEX;
break;
case OBJECT_LANGUAGE:
- tag = "ALTER LANGUAGE";
+ tag = COMMANDTAG_ALTER_LANGUAGE;
break;
case OBJECT_LARGEOBJECT:
- tag = "ALTER LARGE OBJECT";
+ tag = COMMANDTAG_ALTER_LARGE_OBJECT;
break;
case OBJECT_OPCLASS:
- tag = "ALTER OPERATOR CLASS";
+ tag = COMMANDTAG_ALTER_OPERATOR_CLASS;
break;
case OBJECT_OPERATOR:
- tag = "ALTER OPERATOR";
+ tag = COMMANDTAG_ALTER_OPERATOR;
break;
case OBJECT_OPFAMILY:
- tag = "ALTER OPERATOR FAMILY";
+ tag = COMMANDTAG_ALTER_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "ALTER POLICY";
+ tag = COMMANDTAG_ALTER_POLICY;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = COMMANDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROLE:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = COMMANDTAG_ALTER_ROUTINE;
break;
case OBJECT_RULE:
- tag = "ALTER RULE";
+ tag = COMMANDTAG_ALTER_RULE;
break;
case OBJECT_SCHEMA:
- tag = "ALTER SCHEMA";
+ tag = COMMANDTAG_ALTER_SCHEMA;
break;
case OBJECT_SEQUENCE:
- tag = "ALTER SEQUENCE";
+ tag = COMMANDTAG_ALTER_SEQUENCE;
break;
case OBJECT_TABLE:
case OBJECT_TABCONSTRAINT:
- tag = "ALTER TABLE";
+ tag = COMMANDTAG_ALTER_TABLE;
break;
case OBJECT_TABLESPACE:
- tag = "ALTER TABLESPACE";
+ tag = COMMANDTAG_ALTER_TABLESPACE;
break;
case OBJECT_TRIGGER:
- tag = "ALTER TRIGGER";
+ tag = COMMANDTAG_ALTER_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "ALTER EVENT TRIGGER";
+ tag = COMMANDTAG_ALTER_EVENT_TRIGGER;
break;
case OBJECT_TSCONFIGURATION:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_TSDICTIONARY:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSPARSER:
- tag = "ALTER TEXT SEARCH PARSER";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSTEMPLATE:
- tag = "ALTER TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TYPE:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case OBJECT_VIEW:
- tag = "ALTER VIEW";
+ tag = COMMANDTAG_ALTER_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "ALTER MATERIALIZED VIEW";
+ tag = COMMANDTAG_ALTER_MATERIALIZED_VIEW;
break;
case OBJECT_PUBLICATION:
- tag = "ALTER PUBLICATION";
+ tag = COMMANDTAG_ALTER_PUBLICATION;
break;
case OBJECT_SUBSCRIPTION:
- tag = "ALTER SUBSCRIPTION";
+ tag = COMMANDTAG_ALTER_SUBSCRIPTION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "ALTER STATISTICS";
+ tag = COMMANDTAG_ALTER_STATISTICS;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
@@ -2238,20 +2277,17 @@ AlterObjectTypeCommandTag(ObjectType objtype)
/*
* CreateCommandTag
- * utility to get a string representation of the command operation,
+ * utility to get a CommandTag for the command operation,
* given either a raw (un-analyzed) parsetree, an analyzed Query,
* or a PlannedStmt.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
- *
- * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
- * Also, the result must point at a true constant (permanent storage).
*/
-const char *
+CommandTag
CreateCommandTag(Node *parsetree)
{
- const char *tag;
+ CommandTag tag;
switch (nodeTag(parsetree))
{
@@ -2262,19 +2298,19 @@ CreateCommandTag(Node *parsetree)
/* raw plannable queries */
case T_InsertStmt:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case T_DeleteStmt:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case T_UpdateStmt:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case T_SelectStmt:
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
/* utility statements --- same whether raw or cooked */
@@ -2285,51 +2321,51 @@ CreateCommandTag(Node *parsetree)
switch (stmt->kind)
{
case TRANS_STMT_BEGIN:
- tag = "BEGIN";
+ tag = COMMANDTAG_BEGIN;
break;
case TRANS_STMT_START:
- tag = "START TRANSACTION";
+ tag = COMMANDTAG_START_TRANSACTION;
break;
case TRANS_STMT_COMMIT:
- tag = "COMMIT";
+ tag = COMMANDTAG_COMMIT;
break;
case TRANS_STMT_ROLLBACK:
case TRANS_STMT_ROLLBACK_TO:
- tag = "ROLLBACK";
+ tag = COMMANDTAG_ROLLBACK;
break;
case TRANS_STMT_SAVEPOINT:
- tag = "SAVEPOINT";
+ tag = COMMANDTAG_SAVEPOINT;
break;
case TRANS_STMT_RELEASE:
- tag = "RELEASE";
+ tag = COMMANDTAG_RELEASE;
break;
case TRANS_STMT_PREPARE:
- tag = "PREPARE TRANSACTION";
+ tag = COMMANDTAG_PREPARE_TRANSACTION;
break;
case TRANS_STMT_COMMIT_PREPARED:
- tag = "COMMIT PREPARED";
+ tag = COMMANDTAG_COMMIT_PREPARED;
break;
case TRANS_STMT_ROLLBACK_PREPARED:
- tag = "ROLLBACK PREPARED";
+ tag = COMMANDTAG_ROLLBACK_PREPARED;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
break;
case T_DeclareCursorStmt:
- tag = "DECLARE CURSOR";
+ tag = COMMANDTAG_DECLARE_CURSOR;
break;
case T_ClosePortalStmt:
@@ -2337,9 +2373,9 @@ CreateCommandTag(Node *parsetree)
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
if (stmt->portalname == NULL)
- tag = "CLOSE CURSOR ALL";
+ tag = COMMANDTAG_CLOSE_CURSOR_ALL;
else
- tag = "CLOSE CURSOR";
+ tag = COMMANDTAG_CLOSE_CURSOR;
}
break;
@@ -2347,209 +2383,209 @@ CreateCommandTag(Node *parsetree)
{
FetchStmt *stmt = (FetchStmt *) parsetree;
- tag = (stmt->ismove) ? "MOVE" : "FETCH";
+ tag = (stmt->ismove) ? COMMANDTAG_MOVE : COMMANDTAG_FETCH;
}
break;
case T_CreateDomainStmt:
- tag = "CREATE DOMAIN";
+ tag = COMMANDTAG_CREATE_DOMAIN;
break;
case T_CreateSchemaStmt:
- tag = "CREATE SCHEMA";
+ tag = COMMANDTAG_CREATE_SCHEMA;
break;
case T_CreateStmt:
- tag = "CREATE TABLE";
+ tag = COMMANDTAG_CREATE_TABLE;
break;
case T_CreateTableSpaceStmt:
- tag = "CREATE TABLESPACE";
+ tag = COMMANDTAG_CREATE_TABLESPACE;
break;
case T_DropTableSpaceStmt:
- tag = "DROP TABLESPACE";
+ tag = COMMANDTAG_DROP_TABLESPACE;
break;
case T_AlterTableSpaceOptionsStmt:
- tag = "ALTER TABLESPACE";
+ tag = COMMANDTAG_ALTER_TABLESPACE;
break;
case T_CreateExtensionStmt:
- tag = "CREATE EXTENSION";
+ tag = COMMANDTAG_CREATE_EXTENSION;
break;
case T_AlterExtensionStmt:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case T_AlterExtensionContentsStmt:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case T_CreateFdwStmt:
- tag = "CREATE FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER;
break;
case T_AlterFdwStmt:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case T_CreateForeignServerStmt:
- tag = "CREATE SERVER";
+ tag = COMMANDTAG_CREATE_SERVER;
break;
case T_AlterForeignServerStmt:
- tag = "ALTER SERVER";
+ tag = COMMANDTAG_ALTER_SERVER;
break;
case T_CreateUserMappingStmt:
- tag = "CREATE USER MAPPING";
+ tag = COMMANDTAG_CREATE_USER_MAPPING;
break;
case T_AlterUserMappingStmt:
- tag = "ALTER USER MAPPING";
+ tag = COMMANDTAG_ALTER_USER_MAPPING;
break;
case T_DropUserMappingStmt:
- tag = "DROP USER MAPPING";
+ tag = COMMANDTAG_DROP_USER_MAPPING;
break;
case T_CreateForeignTableStmt:
- tag = "CREATE FOREIGN TABLE";
+ tag = COMMANDTAG_CREATE_FOREIGN_TABLE;
break;
case T_ImportForeignSchemaStmt:
- tag = "IMPORT FOREIGN SCHEMA";
+ tag = COMMANDTAG_IMPORT_FOREIGN_SCHEMA;
break;
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_TABLE:
- tag = "DROP TABLE";
+ tag = COMMANDTAG_DROP_TABLE;
break;
case OBJECT_SEQUENCE:
- tag = "DROP SEQUENCE";
+ tag = COMMANDTAG_DROP_SEQUENCE;
break;
case OBJECT_VIEW:
- tag = "DROP VIEW";
+ tag = COMMANDTAG_DROP_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "DROP MATERIALIZED VIEW";
+ tag = COMMANDTAG_DROP_MATERIALIZED_VIEW;
break;
case OBJECT_INDEX:
- tag = "DROP INDEX";
+ tag = COMMANDTAG_DROP_INDEX;
break;
case OBJECT_TYPE:
- tag = "DROP TYPE";
+ tag = COMMANDTAG_DROP_TYPE;
break;
case OBJECT_DOMAIN:
- tag = "DROP DOMAIN";
+ tag = COMMANDTAG_DROP_DOMAIN;
break;
case OBJECT_COLLATION:
- tag = "DROP COLLATION";
+ tag = COMMANDTAG_DROP_COLLATION;
break;
case OBJECT_CONVERSION:
- tag = "DROP CONVERSION";
+ tag = COMMANDTAG_DROP_CONVERSION;
break;
case OBJECT_SCHEMA:
- tag = "DROP SCHEMA";
+ tag = COMMANDTAG_DROP_SCHEMA;
break;
case OBJECT_TSPARSER:
- tag = "DROP TEXT SEARCH PARSER";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "DROP TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "DROP TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "DROP TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "DROP FOREIGN TABLE";
+ tag = COMMANDTAG_DROP_FOREIGN_TABLE;
break;
case OBJECT_EXTENSION:
- tag = "DROP EXTENSION";
+ tag = COMMANDTAG_DROP_EXTENSION;
break;
case OBJECT_FUNCTION:
- tag = "DROP FUNCTION";
+ tag = COMMANDTAG_DROP_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "DROP PROCEDURE";
+ tag = COMMANDTAG_DROP_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "DROP ROUTINE";
+ tag = COMMANDTAG_DROP_ROUTINE;
break;
case OBJECT_AGGREGATE:
- tag = "DROP AGGREGATE";
+ tag = COMMANDTAG_DROP_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "DROP OPERATOR";
+ tag = COMMANDTAG_DROP_OPERATOR;
break;
case OBJECT_LANGUAGE:
- tag = "DROP LANGUAGE";
+ tag = COMMANDTAG_DROP_LANGUAGE;
break;
case OBJECT_CAST:
- tag = "DROP CAST";
+ tag = COMMANDTAG_DROP_CAST;
break;
case OBJECT_TRIGGER:
- tag = "DROP TRIGGER";
+ tag = COMMANDTAG_DROP_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "DROP EVENT TRIGGER";
+ tag = COMMANDTAG_DROP_EVENT_TRIGGER;
break;
case OBJECT_RULE:
- tag = "DROP RULE";
+ tag = COMMANDTAG_DROP_RULE;
break;
case OBJECT_FDW:
- tag = "DROP FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "DROP SERVER";
+ tag = COMMANDTAG_DROP_SERVER;
break;
case OBJECT_OPCLASS:
- tag = "DROP OPERATOR CLASS";
+ tag = COMMANDTAG_DROP_OPERATOR_CLASS;
break;
case OBJECT_OPFAMILY:
- tag = "DROP OPERATOR FAMILY";
+ tag = COMMANDTAG_DROP_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "DROP POLICY";
+ tag = COMMANDTAG_DROP_POLICY;
break;
case OBJECT_TRANSFORM:
- tag = "DROP TRANSFORM";
+ tag = COMMANDTAG_DROP_TRANSFORM;
break;
case OBJECT_ACCESS_METHOD:
- tag = "DROP ACCESS METHOD";
+ tag = COMMANDTAG_DROP_ACCESS_METHOD;
break;
case OBJECT_PUBLICATION:
- tag = "DROP PUBLICATION";
+ tag = COMMANDTAG_DROP_PUBLICATION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "DROP STATISTICS";
+ tag = COMMANDTAG_DROP_STATISTICS;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_TruncateStmt:
- tag = "TRUNCATE TABLE";
+ tag = COMMANDTAG_TRUNCATE_TABLE;
break;
case T_CommentStmt:
- tag = "COMMENT";
+ tag = COMMANDTAG_COMMENT;
break;
case T_SecLabelStmt:
- tag = "SECURITY LABEL";
+ tag = COMMANDTAG_SECURITY_LABEL;
break;
case T_CopyStmt:
- tag = "COPY";
+ tag = COMMANDTAG_COPY;
break;
case T_RenameStmt:
@@ -2584,23 +2620,23 @@ CreateCommandTag(Node *parsetree)
break;
case T_AlterDomainStmt:
- tag = "ALTER DOMAIN";
+ tag = COMMANDTAG_ALTER_DOMAIN;
break;
case T_AlterFunctionStmt:
switch (((AlterFunctionStmt *) parsetree)->objtype)
{
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = COMMANDTAG_ALTER_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = COMMANDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = COMMANDTAG_ALTER_ROUTINE;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
@@ -2608,7 +2644,7 @@ CreateCommandTag(Node *parsetree)
{
GrantStmt *stmt = (GrantStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
+ tag = (stmt->is_grant) ? COMMANDTAG_GRANT : COMMANDTAG_REVOKE;
}
break;
@@ -2616,145 +2652,145 @@ CreateCommandTag(Node *parsetree)
{
GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
+ tag = (stmt->is_grant) ? COMMANDTAG_GRANT_ROLE : COMMANDTAG_REVOKE_ROLE;
}
break;
case T_AlterDefaultPrivilegesStmt:
- tag = "ALTER DEFAULT PRIVILEGES";
+ tag = COMMANDTAG_ALTER_DEFAULT_PRIVILEGES;
break;
case T_DefineStmt:
switch (((DefineStmt *) parsetree)->kind)
{
case OBJECT_AGGREGATE:
- tag = "CREATE AGGREGATE";
+ tag = COMMANDTAG_CREATE_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "CREATE OPERATOR";
+ tag = COMMANDTAG_CREATE_OPERATOR;
break;
case OBJECT_TYPE:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case OBJECT_TSPARSER:
- tag = "CREATE TEXT SEARCH PARSER";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "CREATE TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "CREATE TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "CREATE TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_COLLATION:
- tag = "CREATE COLLATION";
+ tag = COMMANDTAG_CREATE_COLLATION;
break;
case OBJECT_ACCESS_METHOD:
- tag = "CREATE ACCESS METHOD";
+ tag = COMMANDTAG_CREATE_ACCESS_METHOD;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_CompositeTypeStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_CreateEnumStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_CreateRangeStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_AlterEnumStmt:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case T_ViewStmt:
- tag = "CREATE VIEW";
+ tag = COMMANDTAG_CREATE_VIEW;
break;
case T_CreateFunctionStmt:
if (((CreateFunctionStmt *) parsetree)->is_procedure)
- tag = "CREATE PROCEDURE";
+ tag = COMMANDTAG_CREATE_PROCEDURE;
else
- tag = "CREATE FUNCTION";
+ tag = COMMANDTAG_CREATE_FUNCTION;
break;
case T_IndexStmt:
- tag = "CREATE INDEX";
+ tag = COMMANDTAG_CREATE_INDEX;
break;
case T_RuleStmt:
- tag = "CREATE RULE";
+ tag = COMMANDTAG_CREATE_RULE;
break;
case T_CreateSeqStmt:
- tag = "CREATE SEQUENCE";
+ tag = COMMANDTAG_CREATE_SEQUENCE;
break;
case T_AlterSeqStmt:
- tag = "ALTER SEQUENCE";
+ tag = COMMANDTAG_ALTER_SEQUENCE;
break;
case T_DoStmt:
- tag = "DO";
+ tag = COMMANDTAG_DO;
break;
case T_CreatedbStmt:
- tag = "CREATE DATABASE";
+ tag = COMMANDTAG_CREATE_DATABASE;
break;
case T_AlterDatabaseStmt:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case T_AlterDatabaseSetStmt:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case T_DropdbStmt:
- tag = "DROP DATABASE";
+ tag = COMMANDTAG_DROP_DATABASE;
break;
case T_NotifyStmt:
- tag = "NOTIFY";
+ tag = COMMANDTAG_NOTIFY;
break;
case T_ListenStmt:
- tag = "LISTEN";
+ tag = COMMANDTAG_LISTEN;
break;
case T_UnlistenStmt:
- tag = "UNLISTEN";
+ tag = COMMANDTAG_UNLISTEN;
break;
case T_LoadStmt:
- tag = "LOAD";
+ tag = COMMANDTAG_LOAD;
break;
case T_CallStmt:
- tag = "CALL";
+ tag = COMMANDTAG_CALL;
break;
case T_ClusterStmt:
- tag = "CLUSTER";
+ tag = COMMANDTAG_CLUSTER;
break;
case T_VacuumStmt:
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
- tag = "VACUUM";
+ tag = COMMANDTAG_VACUUM;
else
- tag = "ANALYZE";
+ tag = COMMANDTAG_ANALYZE;
break;
case T_ExplainStmt:
- tag = "EXPLAIN";
+ tag = COMMANDTAG_EXPLAIN;
break;
case T_CreateTableAsStmt:
@@ -2762,24 +2798,24 @@ CreateCommandTag(Node *parsetree)
{
case OBJECT_TABLE:
if (((CreateTableAsStmt *) parsetree)->is_select_into)
- tag = "SELECT INTO";
+ tag = COMMANDTAG_SELECT_INTO;
else
- tag = "CREATE TABLE AS";
+ tag = COMMANDTAG_CREATE_TABLE_AS;
break;
case OBJECT_MATVIEW:
- tag = "CREATE MATERIALIZED VIEW";
+ tag = COMMANDTAG_CREATE_MATERIALIZED_VIEW;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_RefreshMatViewStmt:
- tag = "REFRESH MATERIALIZED VIEW";
+ tag = COMMANDTAG_REFRESH_MATERIALIZED_VIEW;
break;
case T_AlterSystemStmt:
- tag = "ALTER SYSTEM";
+ tag = COMMANDTAG_ALTER_SYSTEM;
break;
case T_VariableSetStmt:
@@ -2789,183 +2825,183 @@ CreateCommandTag(Node *parsetree)
case VAR_SET_CURRENT:
case VAR_SET_DEFAULT:
case VAR_SET_MULTI:
- tag = "SET";
+ tag = COMMANDTAG_SET;
break;
case VAR_RESET:
case VAR_RESET_ALL:
- tag = "RESET";
+ tag = COMMANDTAG_RESET;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_VariableShowStmt:
- tag = "SHOW";
+ tag = COMMANDTAG_SHOW;
break;
case T_DiscardStmt:
switch (((DiscardStmt *) parsetree)->target)
{
case DISCARD_ALL:
- tag = "DISCARD ALL";
+ tag = COMMANDTAG_DISCARD_ALL;
break;
case DISCARD_PLANS:
- tag = "DISCARD PLANS";
+ tag = COMMANDTAG_DISCARD_PLANS;
break;
case DISCARD_TEMP:
- tag = "DISCARD TEMP";
+ tag = COMMANDTAG_DISCARD_TEMP;
break;
case DISCARD_SEQUENCES:
- tag = "DISCARD SEQUENCES";
+ tag = COMMANDTAG_DISCARD_SEQUENCES;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_CreateTransformStmt:
- tag = "CREATE TRANSFORM";
+ tag = COMMANDTAG_CREATE_TRANSFORM;
break;
case T_CreateTrigStmt:
- tag = "CREATE TRIGGER";
+ tag = COMMANDTAG_CREATE_TRIGGER;
break;
case T_CreateEventTrigStmt:
- tag = "CREATE EVENT TRIGGER";
+ tag = COMMANDTAG_CREATE_EVENT_TRIGGER;
break;
case T_AlterEventTrigStmt:
- tag = "ALTER EVENT TRIGGER";
+ tag = COMMANDTAG_ALTER_EVENT_TRIGGER;
break;
case T_CreatePLangStmt:
- tag = "CREATE LANGUAGE";
+ tag = COMMANDTAG_CREATE_LANGUAGE;
break;
case T_CreateRoleStmt:
- tag = "CREATE ROLE";
+ tag = COMMANDTAG_CREATE_ROLE;
break;
case T_AlterRoleStmt:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case T_AlterRoleSetStmt:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case T_DropRoleStmt:
- tag = "DROP ROLE";
+ tag = COMMANDTAG_DROP_ROLE;
break;
case T_DropOwnedStmt:
- tag = "DROP OWNED";
+ tag = COMMANDTAG_DROP_OWNED;
break;
case T_ReassignOwnedStmt:
- tag = "REASSIGN OWNED";
+ tag = COMMANDTAG_REASSIGN_OWNED;
break;
case T_LockStmt:
- tag = "LOCK TABLE";
+ tag = COMMANDTAG_LOCK_TABLE;
break;
case T_ConstraintsSetStmt:
- tag = "SET CONSTRAINTS";
+ tag = COMMANDTAG_SET_CONSTRAINTS;
break;
case T_CheckPointStmt:
- tag = "CHECKPOINT";
+ tag = COMMANDTAG_CHECKPOINT;
break;
case T_ReindexStmt:
- tag = "REINDEX";
+ tag = COMMANDTAG_REINDEX;
break;
case T_CreateConversionStmt:
- tag = "CREATE CONVERSION";
+ tag = COMMANDTAG_CREATE_CONVERSION;
break;
case T_CreateCastStmt:
- tag = "CREATE CAST";
+ tag = COMMANDTAG_CREATE_CAST;
break;
case T_CreateOpClassStmt:
- tag = "CREATE OPERATOR CLASS";
+ tag = COMMANDTAG_CREATE_OPERATOR_CLASS;
break;
case T_CreateOpFamilyStmt:
- tag = "CREATE OPERATOR FAMILY";
+ tag = COMMANDTAG_CREATE_OPERATOR_FAMILY;
break;
case T_AlterOpFamilyStmt:
- tag = "ALTER OPERATOR FAMILY";
+ tag = COMMANDTAG_ALTER_OPERATOR_FAMILY;
break;
case T_AlterOperatorStmt:
- tag = "ALTER OPERATOR";
+ tag = COMMANDTAG_ALTER_OPERATOR;
break;
case T_AlterTSDictionaryStmt:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case T_AlterTSConfigurationStmt:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case T_CreatePolicyStmt:
- tag = "CREATE POLICY";
+ tag = COMMANDTAG_CREATE_POLICY;
break;
case T_AlterPolicyStmt:
- tag = "ALTER POLICY";
+ tag = COMMANDTAG_ALTER_POLICY;
break;
case T_CreateAmStmt:
- tag = "CREATE ACCESS METHOD";
+ tag = COMMANDTAG_CREATE_ACCESS_METHOD;
break;
case T_CreatePublicationStmt:
- tag = "CREATE PUBLICATION";
+ tag = COMMANDTAG_CREATE_PUBLICATION;
break;
case T_AlterPublicationStmt:
- tag = "ALTER PUBLICATION";
+ tag = COMMANDTAG_ALTER_PUBLICATION;
break;
case T_CreateSubscriptionStmt:
- tag = "CREATE SUBSCRIPTION";
+ tag = COMMANDTAG_CREATE_SUBSCRIPTION;
break;
case T_AlterSubscriptionStmt:
- tag = "ALTER SUBSCRIPTION";
+ tag = COMMANDTAG_ALTER_SUBSCRIPTION;
break;
case T_DropSubscriptionStmt:
- tag = "DROP SUBSCRIPTION";
+ tag = COMMANDTAG_DROP_SUBSCRIPTION;
break;
case T_AlterCollationStmt:
- tag = "ALTER COLLATION";
+ tag = COMMANDTAG_ALTER_COLLATION;
break;
case T_PrepareStmt:
- tag = "PREPARE";
+ tag = COMMANDTAG_PREPARE;
break;
case T_ExecuteStmt:
- tag = "EXECUTE";
+ tag = COMMANDTAG_EXECUTE;
break;
case T_CreateStatsStmt:
- tag = "CREATE STATISTICS";
+ tag = COMMANDTAG_CREATE_STATISTICS;
break;
case T_AlterStatsStmt:
- tag = "ALTER STATISTICS";
+ tag = COMMANDTAG_ALTER_STATISTICS;
break;
case T_DeallocateStmt:
@@ -2973,9 +3009,9 @@ CreateCommandTag(Node *parsetree)
DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
if (stmt->name == NULL)
- tag = "DEALLOCATE ALL";
+ tag = COMMANDTAG_DEALLOCATE_ALL;
else
- tag = "DEALLOCATE";
+ tag = COMMANDTAG_DEALLOCATE;
}
break;
@@ -2999,33 +3035,33 @@ CreateCommandTag(Node *parsetree)
switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = COMMANDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = COMMANDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
}
}
else
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3033,7 +3069,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
@@ -3059,33 +3095,33 @@ CreateCommandTag(Node *parsetree)
switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = COMMANDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = COMMANDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
else
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3093,7 +3129,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
@@ -3102,7 +3138,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c
index 33272f8030..37e3cc70f8 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/txid.c
@@ -425,7 +425,7 @@ txid_current(PG_FUNCTION_ARGS)
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
+ PreventCommandStrDuringRecovery("txid_current()");
load_xid_epoch(&state);
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index c47be0ba4c..53401bd4e9 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -163,7 +163,7 @@ InitPlanCache(void)
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
MemoryContext source_context;
@@ -246,7 +246,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
CachedPlanSource *
CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index b675575c31..b1c45ca369 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -281,7 +281,7 @@ void
PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan)
{
@@ -289,10 +289,12 @@ PortalDefineQuery(Portal portal,
AssertState(portal->status == PORTAL_NEW);
AssertArg(sourceText != NULL);
- AssertArg(commandTag != NULL || stmts == NIL);
+ AssertArg(commandTag != COMMANDTAG_NULL || stmts == NIL);
portal->prepStmtName = prepStmtName;
portal->sourceText = sourceText;
+ Assert(COMPLETION_TAG_BUFSIZE > 0);
+ portal->completionTag[0] = '\0';
portal->commandTag = commandTag;
portal->stmts = stmts;
portal->cplan = cplan;
diff --git a/src/common/Makefile b/src/common/Makefile
index e757fb7399..30a0f5c559 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -48,6 +48,7 @@ LIBS += $(PTHREAD_LIBS)
OBJS_COMMON = \
base64.o \
+ commandtag.o \
config_info.o \
controldata_utils.o \
d2s.o \
diff --git a/src/common/commandtag.c b/src/common/commandtag.c
new file mode 100644
index 0000000000..09455f7f1f
--- /dev/null
+++ b/src/common/commandtag.c
@@ -0,0 +1,382 @@
+/*-------------------------------------------------------------------------
+ *
+ * commandtag.c
+ * Data and routines for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/common/commandtag.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef FRONTEND
+#include "postgres_fe.h"
+#define elog(...)
+#else
+#include "postgres.h"
+#endif
+
+#include "c.h"
+#include "common/commandtag.h"
+
+typedef struct command_tag_name_map
+{
+ CommandTag commandTag;
+ const char *commandname;
+} command_tag_name_map;
+
+const command_tag_name_map tag_names[] = {
+ {COMMANDTAG_NULL, ""},
+ {COMMANDTAG_UNKNOWN, "???"},
+ {COMMANDTAG_ALTER_ACCESS_METHOD, "ALTER ACCESS METHOD"},
+ {COMMANDTAG_ALTER_AGGREGATE, "ALTER AGGREGATE"},
+ {COMMANDTAG_ALTER_CAST, "ALTER CAST"},
+ {COMMANDTAG_ALTER_COLLATION, "ALTER COLLATION"},
+ {COMMANDTAG_ALTER_CONSTRAINT, "ALTER CONSTRAINT"},
+ {COMMANDTAG_ALTER_CONVERSION, "ALTER CONVERSION"},
+ {COMMANDTAG_ALTER_DATABASE, "ALTER DATABASE"},
+ {COMMANDTAG_ALTER_DEFAULT_PRIVILEGES, "ALTER DEFAULT PRIVILEGES"},
+ {COMMANDTAG_ALTER_DOMAIN, "ALTER DOMAIN"},
+ {COMMANDTAG_ALTER_EVENT_TRIGGER, "ALTER EVENT TRIGGER"},
+ {COMMANDTAG_ALTER_EXTENSION, "ALTER EXTENSION"},
+ {COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER, "ALTER FOREIGN DATA WRAPPER"},
+ {COMMANDTAG_ALTER_FOREIGN_TABLE, "ALTER FOREIGN TABLE"},
+ {COMMANDTAG_ALTER_FUNCTION, "ALTER FUNCTION"},
+ {COMMANDTAG_ALTER_INDEX, "ALTER INDEX"},
+ {COMMANDTAG_ALTER_LANGUAGE, "ALTER LANGUAGE"},
+ {COMMANDTAG_ALTER_LARGE_OBJECT, "ALTER LARGE OBJECT"},
+ {COMMANDTAG_ALTER_MATERIALIZED_VIEW, "ALTER MATERIALIZED VIEW"},
+ {COMMANDTAG_ALTER_OPERATOR, "ALTER OPERATOR"},
+ {COMMANDTAG_ALTER_OPERATOR_CLASS, "ALTER OPERATOR CLASS"},
+ {COMMANDTAG_ALTER_OPERATOR_FAMILY, "ALTER OPERATOR FAMILY"},
+ {COMMANDTAG_ALTER_POLICY, "ALTER POLICY"},
+ {COMMANDTAG_ALTER_PROCEDURE, "ALTER PROCEDURE"},
+ {COMMANDTAG_ALTER_PUBLICATION, "ALTER PUBLICATION"},
+ {COMMANDTAG_ALTER_ROLE, "ALTER ROLE"},
+ {COMMANDTAG_ALTER_ROUTINE, "ALTER ROUTINE"},
+ {COMMANDTAG_ALTER_RULE, "ALTER RULE"},
+ {COMMANDTAG_ALTER_SCHEMA, "ALTER SCHEMA"},
+ {COMMANDTAG_ALTER_SEQUENCE, "ALTER SEQUENCE"},
+ {COMMANDTAG_ALTER_SERVER, "ALTER SERVER"},
+ {COMMANDTAG_ALTER_STATISTICS, "ALTER STATISTICS"},
+ {COMMANDTAG_ALTER_SUBSCRIPTION, "ALTER SUBSCRIPTION"},
+ {COMMANDTAG_ALTER_SYSTEM, "ALTER SYSTEM"},
+ {COMMANDTAG_ALTER_TABLE, "ALTER TABLE"},
+ {COMMANDTAG_ALTER_TABLESPACE, "ALTER TABLESPACE"},
+ {COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION, "ALTER TEXT SEARCH CONFIGURATION"},
+ {COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY, "ALTER TEXT SEARCH DICTIONARY"},
+ {COMMANDTAG_ALTER_TEXT_SEARCH_PARSER, "ALTER TEXT SEARCH PARSER"},
+ {COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE, "ALTER TEXT SEARCH TEMPLATE"},
+ {COMMANDTAG_ALTER_TRANSFORM, "ALTER TRANSFORM"},
+ {COMMANDTAG_ALTER_TRIGGER, "ALTER TRIGGER"},
+ {COMMANDTAG_ALTER_TYPE, "ALTER TYPE"},
+ {COMMANDTAG_ALTER_USER_MAPPING, "ALTER USER MAPPING"},
+ {COMMANDTAG_ALTER_VIEW, "ALTER VIEW"},
+ {COMMANDTAG_ANALYZE, "ANALYZE"},
+ {COMMANDTAG_BEGIN, "BEGIN"},
+ {COMMANDTAG_CALL, "CALL"},
+ {COMMANDTAG_CHECKPOINT, "CHECKPOINT"},
+ {COMMANDTAG_CLOSE, "CLOSE"},
+ {COMMANDTAG_CLOSE_CURSOR, "CLOSE CURSOR"},
+ {COMMANDTAG_CLOSE_CURSOR_ALL, "CLOSE CURSOR ALL"},
+ {COMMANDTAG_CLUSTER, "CLUSTER"},
+ {COMMANDTAG_COMMENT, "COMMENT"},
+ {COMMANDTAG_COMMIT, "COMMIT"},
+ {COMMANDTAG_COMMIT_PREPARED, "COMMIT PREPARED"},
+ {COMMANDTAG_COPY, "COPY"},
+ {COMMANDTAG_COPY_FROM, "COPY FROM"},
+ {COMMANDTAG_CREATE_ACCESS_METHOD, "CREATE ACCESS METHOD"},
+ {COMMANDTAG_CREATE_AGGREGATE, "CREATE AGGREGATE"},
+ {COMMANDTAG_CREATE_CAST, "CREATE CAST"},
+ {COMMANDTAG_CREATE_COLLATION, "CREATE COLLATION"},
+ {COMMANDTAG_CREATE_CONSTRAINT, "CREATE CONSTRAINT"},
+ {COMMANDTAG_CREATE_CONVERSION, "CREATE CONVERSION"},
+ {COMMANDTAG_CREATE_DATABASE, "CREATE DATABASE"},
+ {COMMANDTAG_CREATE_DOMAIN, "CREATE DOMAIN"},
+ {COMMANDTAG_CREATE_EVENT_TRIGGER, "CREATE EVENT TRIGGER"},
+ {COMMANDTAG_CREATE_EXTENSION, "CREATE EXTENSION"},
+ {COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER, "CREATE FOREIGN DATA WRAPPER"},
+ {COMMANDTAG_CREATE_FOREIGN_TABLE, "CREATE FOREIGN TABLE"},
+ {COMMANDTAG_CREATE_FUNCTION, "CREATE FUNCTION"},
+ {COMMANDTAG_CREATE_INDEX, "CREATE INDEX"},
+ {COMMANDTAG_CREATE_LANGUAGE, "CREATE LANGUAGE"},
+ {COMMANDTAG_CREATE_MATERIALIZED_VIEW, "CREATE MATERIALIZED VIEW"},
+ {COMMANDTAG_CREATE_OPERATOR, "CREATE OPERATOR"},
+ {COMMANDTAG_CREATE_OPERATOR_CLASS, "CREATE OPERATOR CLASS"},
+ {COMMANDTAG_CREATE_OPERATOR_FAMILY, "CREATE OPERATOR FAMILY"},
+ {COMMANDTAG_CREATE_POLICY, "CREATE POLICY"},
+ {COMMANDTAG_CREATE_PROCEDURE, "CREATE PROCEDURE"},
+ {COMMANDTAG_CREATE_PUBLICATION, "CREATE PUBLICATION"},
+ {COMMANDTAG_CREATE_ROLE, "CREATE ROLE"},
+ {COMMANDTAG_CREATE_ROUTINE, "CREATE ROUTINE"},
+ {COMMANDTAG_CREATE_RULE, "CREATE RULE"},
+ {COMMANDTAG_CREATE_SCHEMA, "CREATE SCHEMA"},
+ {COMMANDTAG_CREATE_SEQUENCE, "CREATE SEQUENCE"},
+ {COMMANDTAG_CREATE_SERVER, "CREATE SERVER"},
+ {COMMANDTAG_CREATE_STATISTICS, "CREATE STATISTICS"},
+ {COMMANDTAG_CREATE_SUBSCRIPTION, "CREATE SUBSCRIPTION"},
+ {COMMANDTAG_CREATE_TABLE, "CREATE TABLE"},
+ {COMMANDTAG_CREATE_TABLE_AS, "CREATE TABLE AS"},
+ {COMMANDTAG_CREATE_TABLESPACE, "CREATE TABLESPACE"},
+ {COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION, "CREATE TEXT SEARCH CONFIGURATION"},
+ {COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY, "CREATE TEXT SEARCH DICTIONARY"},
+ {COMMANDTAG_CREATE_TEXT_SEARCH_PARSER, "CREATE TEXT SEARCH PARSER"},
+ {COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE, "CREATE TEXT SEARCH TEMPLATE"},
+ {COMMANDTAG_CREATE_TRANSFORM, "CREATE TRANSFORM"},
+ {COMMANDTAG_CREATE_TRIGGER, "CREATE TRIGGER"},
+ {COMMANDTAG_CREATE_TYPE, "CREATE TYPE"},
+ {COMMANDTAG_CREATE_USER_MAPPING, "CREATE USER MAPPING"},
+ {COMMANDTAG_CREATE_VIEW, "CREATE VIEW"},
+ {COMMANDTAG_DEALLOCATE, "DEALLOCATE"},
+ {COMMANDTAG_DEALLOCATE_ALL, "DEALLOCATE ALL"},
+ {COMMANDTAG_DECLARE_CURSOR, "DECLARE CURSOR"},
+ {COMMANDTAG_DELETE, "DELETE"},
+ {COMMANDTAG_DISCARD, "DISCARD"},
+ {COMMANDTAG_DISCARD_ALL, "DISCARD ALL"},
+ {COMMANDTAG_DISCARD_PLANS, "DISCARD PLANS"},
+ {COMMANDTAG_DISCARD_SEQUENCES, "DISCARD SEQUENCES"},
+ {COMMANDTAG_DISCARD_TEMP, "DISCARD TEMP"},
+ {COMMANDTAG_DO, "DO"},
+ {COMMANDTAG_DROP_ACCESS_METHOD, "DROP ACCESS METHOD"},
+ {COMMANDTAG_DROP_AGGREGATE, "DROP AGGREGATE"},
+ {COMMANDTAG_DROP_CAST, "DROP CAST"},
+ {COMMANDTAG_DROP_COLLATION, "DROP COLLATION"},
+ {COMMANDTAG_DROP_CONSTRAINT, "DROP CONSTRAINT"},
+ {COMMANDTAG_DROP_CONVERSION, "DROP CONVERSION"},
+ {COMMANDTAG_DROP_DATABASE, "DROP DATABASE"},
+ {COMMANDTAG_DROP_DOMAIN, "DROP DOMAIN"},
+ {COMMANDTAG_DROP_EVENT_TRIGGER, "DROP EVENT TRIGGER"},
+ {COMMANDTAG_DROP_EXTENSION, "DROP EXTENSION"},
+ {COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER, "DROP FOREIGN DATA WRAPPER"},
+ {COMMANDTAG_DROP_FOREIGN_TABLE, "DROP FOREIGN TABLE"},
+ {COMMANDTAG_DROP_FUNCTION, "DROP FUNCTION"},
+ {COMMANDTAG_DROP_INDEX, "DROP INDEX"},
+ {COMMANDTAG_DROP_LANGUAGE, "DROP LANGUAGE"},
+ {COMMANDTAG_DROP_MATERIALIZED_VIEW, "DROP MATERIALIZED VIEW"},
+ {COMMANDTAG_DROP_OPERATOR, "DROP OPERATOR"},
+ {COMMANDTAG_DROP_OPERATOR_CLASS, "DROP OPERATOR CLASS"},
+ {COMMANDTAG_DROP_OPERATOR_FAMILY, "DROP OPERATOR FAMILY"},
+ {COMMANDTAG_DROP_OWNED, "DROP OWNED"},
+ {COMMANDTAG_DROP_POLICY, "DROP POLICY"},
+ {COMMANDTAG_DROP_PROCEDURE, "DROP PROCEDURE"},
+ {COMMANDTAG_DROP_PUBLICATION, "DROP PUBLICATION"},
+ {COMMANDTAG_DROP_REPLICATION_SLOT, "DROP REPLICATION SLOT"},
+ {COMMANDTAG_DROP_ROLE, "DROP ROLE"},
+ {COMMANDTAG_DROP_ROUTINE, "DROP ROUTINE"},
+ {COMMANDTAG_DROP_RULE, "DROP RULE"},
+ {COMMANDTAG_DROP_SCHEMA, "DROP SCHEMA"},
+ {COMMANDTAG_DROP_SEQUENCE, "DROP SEQUENCE"},
+ {COMMANDTAG_DROP_SERVER, "DROP SERVER"},
+ {COMMANDTAG_DROP_STATISTICS, "DROP STATISTICS"},
+ {COMMANDTAG_DROP_SUBSCRIPTION, "DROP SUBSCRIPTION"},
+ {COMMANDTAG_DROP_TABLE, "DROP TABLE"},
+ {COMMANDTAG_DROP_TABLESPACE, "DROP TABLESPACE"},
+ {COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION, "DROP TEXT SEARCH CONFIGURATION"},
+ {COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY, "DROP TEXT SEARCH DICTIONARY"},
+ {COMMANDTAG_DROP_TEXT_SEARCH_PARSER, "DROP TEXT SEARCH PARSER"},
+ {COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE, "DROP TEXT SEARCH TEMPLATE"},
+ {COMMANDTAG_DROP_TRANSFORM, "DROP TRANSFORM"},
+ {COMMANDTAG_DROP_TRIGGER, "DROP TRIGGER"},
+ {COMMANDTAG_DROP_TYPE, "DROP TYPE"},
+ {COMMANDTAG_DROP_USER_MAPPING, "DROP USER MAPPING"},
+ {COMMANDTAG_DROP_VIEW, "DROP VIEW"},
+ {COMMANDTAG_EXECUTE, "EXECUTE"},
+ {COMMANDTAG_EXPLAIN, "EXPLAIN"},
+ {COMMANDTAG_FETCH, "FETCH"},
+ {COMMANDTAG_GRANT, "GRANT"},
+ {COMMANDTAG_GRANT_ROLE, "GRANT ROLE"},
+ {COMMANDTAG_IMPORT_FOREIGN_SCHEMA, "IMPORT FOREIGN SCHEMA"},
+ {COMMANDTAG_INSERT, "INSERT"},
+ {COMMANDTAG_LISTEN, "LISTEN"},
+ {COMMANDTAG_LOAD, "LOAD"},
+ {COMMANDTAG_LOCK_TABLE, "LOCK TABLE"},
+ {COMMANDTAG_MOVE, "MOVE"},
+ {COMMANDTAG_NOTIFY, "NOTIFY"},
+ {COMMANDTAG_PREPARE, "PREPARE"},
+ {COMMANDTAG_PREPARE_TRANSACTION, "PREPARE TRANSACTION"},
+ {COMMANDTAG_REASSIGN_OWNED, "REASSIGN OWNED"},
+ {COMMANDTAG_REFRESH_MATERIALIZED_VIEW, "REFRESH MATERIALIZED VIEW"},
+ {COMMANDTAG_REINDEX, "REINDEX"},
+ {COMMANDTAG_RELEASE, "RELEASE"},
+ {COMMANDTAG_RESET, "RESET"},
+ {COMMANDTAG_REVOKE, "REVOKE"},
+ {COMMANDTAG_REVOKE_ROLE, "REVOKE ROLE"},
+ {COMMANDTAG_ROLLBACK, "ROLLBACK"},
+ {COMMANDTAG_ROLLBACK_PREPARED, "ROLLBACK PREPARED"},
+ {COMMANDTAG_SAVEPOINT, "SAVEPOINT"},
+ {COMMANDTAG_SECURITY_LABEL, "SECURITY LABEL"},
+ {COMMANDTAG_SELECT, "SELECT"},
+ {COMMANDTAG_SELECT_FOR_KEY_SHARE, "SELECT FOR KEY SHARE"},
+ {COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE, "SELECT FOR NO KEY UPDATE"},
+ {COMMANDTAG_SELECT_FOR_SHARE, "SELECT FOR SHARE"},
+ {COMMANDTAG_SELECT_FOR_UPDATE, "SELECT FOR UPDATE"},
+ {COMMANDTAG_SELECT_INTO, "SELECT INTO"},
+ {COMMANDTAG_SET, "SET"},
+ {COMMANDTAG_SET_CONSTRAINTS, "SET CONSTRAINTS"},
+ {COMMANDTAG_SHOW, "SHOW"},
+ {COMMANDTAG_START_TRANSACTION, "START TRANSACTION"},
+ {COMMANDTAG_TRUNCATE_TABLE, "TRUNCATE TABLE"},
+ {COMMANDTAG_UNLISTEN, "UNLISTEN"},
+ {COMMANDTAG_UPDATE, "UPDATE"},
+ {COMMANDTAG_VACUUM, "VACUUM"},
+ {(CommandTag) 0, ""} /* Sentinel */
+};
+
+#ifdef COMMANDTAG_CHECKING
+static bool commandtags_have_been_checked = false;
+static inline void
+OPTIONALLY_CHECK_COMMAND_TAGS()
+{
+ if (!commandtags_have_been_checked)
+ {
+ Assert(CheckCommandTagEnum());
+ commandtags_have_been_checked = true;
+ }
+}
+#else
+#define OPTIONALLY_CHECK_COMMAND_TAGS()
+#endif
+
+/*
+ * Search CommandTag by name
+ *
+ * Returns CommandTag, or COMMANDTAG_UNKNOWN if not recognized
+ */
+CommandTag
+GetCommandTagEnum(const char *commandname)
+{
+ unsigned int tnl = lengthof(tag_names);
+ const command_tag_name_map *base = tag_names,
+ *last = base + tnl - 2,
+ *position;
+ int result;
+
+ OPTIONALLY_CHECK_COMMAND_TAGS();
+
+ if (commandname == NULL || *commandname == '\0')
+ return COMMANDTAG_UNKNOWN;
+
+ while (last >= base)
+ {
+ position = base + ((last - base) >> 1);
+ result = pg_strcasecmp(commandname, position->commandname);
+ if (result == 0)
+ return position->commandTag;
+ else if (result < 0)
+ last = position - 1;
+ else
+ base = position + 1;
+ }
+ return COMMANDTAG_UNKNOWN;
+}
+
+const char *
+GetCommandTagName(CommandTag commandTag)
+{
+ OPTIONALLY_CHECK_COMMAND_TAGS();
+ if (PG_VALID_COMMANDTAG(commandTag))
+ return tag_names[commandTag].commandname;
+ return "???";
+}
+
+#ifdef COMMANDTAG_CHECKING
+bool
+CheckCommandTagEnum()
+{
+ unsigned int tnl = lengthof(tag_names);
+ const command_tag_name_map *first,
+ *last,
+ *sentinel,
+ *position,
+ *alternate;
+ CommandTag commandTag;
+
+ if (FIRST_COMMAND_TAG < 0 || LAST_COMMAND_TAG < 0 || LAST_COMMAND_TAG < FIRST_COMMAND_TAG)
+ {
+ elog(ERROR, "FIRST_COMMAND_TAG (%d), LAST_COMMAND_TAG (%d) not reasonable",
+ (int) FIRST_COMMAND_TAG, (int) LAST_COMMAND_TAG);
+ return false;
+ }
+
+ first = &tag_names[FIRST_COMMAND_TAG];
+ last = &tag_names[LAST_COMMAND_TAG];
+ sentinel = last + 1;
+
+ if (sentinel != tag_names + tnl - 1)
+ {
+ elog(ERROR, "CommandTag sentinel in wrong location");
+ return false;
+ }
+ if (sentinel->commandTag != (CommandTag) 0)
+ {
+ elog(ERROR, "CommandTag sentinel has wrong commandTag value");
+ return false;
+ }
+ if (sentinel->commandname == NULL)
+ {
+ elog(ERROR, "CommandTag sentinel has null commandname");
+ return false;
+ }
+ if (strcmp(sentinel->commandname, "") != 0)
+ {
+ elog(ERROR, "CommandTag sentinel commandname should be \"\"");
+ return false;
+ }
+
+ for (commandTag = FIRST_COMMAND_TAG; commandTag <= LAST_COMMAND_TAG; commandTag++)
+ {
+ if (tag_names[commandTag].commandTag != commandTag)
+ {
+ elog(ERROR, "tag_names[] commandTag field disordered");
+ return false;
+ }
+ }
+
+ for (position = first; position < last; position++)
+ {
+ if (!position->commandname)
+ {
+ elog(ERROR, "CommandTag found with null commandname");
+ return false;
+ }
+ }
+ for (position = first; position < last; position++)
+ {
+ if (position->commandTag < FIRST_COMMAND_TAG)
+ {
+ elog(ERROR, "Found CommandTag before FIRST_COMMAND_TAG");
+ return false;
+ }
+ if (position->commandTag > LAST_COMMAND_TAG)
+ {
+ elog(ERROR, "Found CommandTag after LAST_COMMAND_TAG");
+ return false;
+ }
+ for (alternate = position + 1; alternate <= last; alternate++)
+ {
+ int cmp;
+
+ if (position->commandTag == alternate->commandTag)
+ {
+ elog(ERROR, "Found duplicate commandTags");
+ return false;
+ }
+ cmp = pg_strcasecmp(position->commandname, alternate->commandname);
+ if (cmp == 0)
+ {
+ elog(ERROR, "Found duplicate commandnames");
+ return false;
+ }
+ else if (cmp > 0)
+ {
+ elog(ERROR, "Found commandnames out of order");
+ return false;
+ }
+ }
+ }
+ return true;
+}
+#endif
diff --git a/src/include/common/commandtag.h b/src/include/common/commandtag.h
new file mode 100644
index 0000000000..e0388d2973
--- /dev/null
+++ b/src/include/common/commandtag.h
@@ -0,0 +1,254 @@
+/*-------------------------------------------------------------------------
+ *
+ * commandtag.h
+ * Declarations for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/commandtag.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMMANDTAG_H
+#define COMMANDTAG_H
+
+/*
+ * BEWARE: These are in sorted order, but ordered by their printed
+ * values in command_tag_name_map (see common/commandtag.c).
+ * In particular it matters because the sort ordering changes
+ * when you replace a space with an underscore. To wit:
+ *
+ * "CREATE TABLE"
+ * "CREATE TABLE AS"
+ * "CREATE TABLESPACE"
+ *
+ * but...
+ *
+ * CREATE_TABLE
+ * CREATE_TABLESPACE
+ * CREATE_TABLE_AS
+ *
+ * It also matters that tags NULL and UNKNOWN have
+ * string values "" and "???".
+ *
+ * If you add a value here, add it in common/commandtag.c also, and
+ * be careful to get the ordering right. You can build with
+ * COMMANDTAG_CHECKING to have this automatically checked
+ * at runtime, but that adds considerable overhead, so do so sparingly.
+ */
+typedef enum CommandTag
+{
+#define FIRST_COMMAND_TAG COMMANDTAG_NULL
+ COMMANDTAG_NULL,
+ COMMANDTAG_UNKNOWN,
+ COMMANDTAG_ALTER_ACCESS_METHOD,
+ COMMANDTAG_ALTER_AGGREGATE,
+ COMMANDTAG_ALTER_CAST,
+ COMMANDTAG_ALTER_COLLATION,
+ COMMANDTAG_ALTER_CONSTRAINT,
+ COMMANDTAG_ALTER_CONVERSION,
+ COMMANDTAG_ALTER_DATABASE,
+ COMMANDTAG_ALTER_DEFAULT_PRIVILEGES,
+ COMMANDTAG_ALTER_DOMAIN,
+ COMMANDTAG_ALTER_EVENT_TRIGGER,
+ COMMANDTAG_ALTER_EXTENSION,
+ COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER,
+ COMMANDTAG_ALTER_FOREIGN_TABLE,
+ COMMANDTAG_ALTER_FUNCTION,
+ COMMANDTAG_ALTER_INDEX,
+ COMMANDTAG_ALTER_LANGUAGE,
+ COMMANDTAG_ALTER_LARGE_OBJECT,
+ COMMANDTAG_ALTER_MATERIALIZED_VIEW,
+ COMMANDTAG_ALTER_OPERATOR,
+ COMMANDTAG_ALTER_OPERATOR_CLASS,
+ COMMANDTAG_ALTER_OPERATOR_FAMILY,
+ COMMANDTAG_ALTER_POLICY,
+ COMMANDTAG_ALTER_PROCEDURE,
+ COMMANDTAG_ALTER_PUBLICATION,
+ COMMANDTAG_ALTER_ROLE,
+ COMMANDTAG_ALTER_ROUTINE,
+ COMMANDTAG_ALTER_RULE,
+ COMMANDTAG_ALTER_SCHEMA,
+ COMMANDTAG_ALTER_SEQUENCE,
+ COMMANDTAG_ALTER_SERVER,
+ COMMANDTAG_ALTER_STATISTICS,
+ COMMANDTAG_ALTER_SUBSCRIPTION,
+ COMMANDTAG_ALTER_SYSTEM,
+ COMMANDTAG_ALTER_TABLE,
+ COMMANDTAG_ALTER_TABLESPACE,
+ COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION,
+ COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY,
+ COMMANDTAG_ALTER_TEXT_SEARCH_PARSER,
+ COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE,
+ COMMANDTAG_ALTER_TRANSFORM,
+ COMMANDTAG_ALTER_TRIGGER,
+ COMMANDTAG_ALTER_TYPE,
+ COMMANDTAG_ALTER_USER_MAPPING,
+ COMMANDTAG_ALTER_VIEW,
+ COMMANDTAG_ANALYZE,
+ COMMANDTAG_BEGIN,
+ COMMANDTAG_CALL,
+ COMMANDTAG_CHECKPOINT,
+ COMMANDTAG_CLOSE,
+ COMMANDTAG_CLOSE_CURSOR,
+ COMMANDTAG_CLOSE_CURSOR_ALL,
+ COMMANDTAG_CLUSTER,
+ COMMANDTAG_COMMENT,
+ COMMANDTAG_COMMIT,
+ COMMANDTAG_COMMIT_PREPARED,
+ COMMANDTAG_COPY,
+ COMMANDTAG_COPY_FROM,
+ COMMANDTAG_CREATE_ACCESS_METHOD,
+ COMMANDTAG_CREATE_AGGREGATE,
+ COMMANDTAG_CREATE_CAST,
+ COMMANDTAG_CREATE_COLLATION,
+ COMMANDTAG_CREATE_CONSTRAINT,
+ COMMANDTAG_CREATE_CONVERSION,
+ COMMANDTAG_CREATE_DATABASE,
+ COMMANDTAG_CREATE_DOMAIN,
+ COMMANDTAG_CREATE_EVENT_TRIGGER,
+ COMMANDTAG_CREATE_EXTENSION,
+ COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER,
+ COMMANDTAG_CREATE_FOREIGN_TABLE,
+ COMMANDTAG_CREATE_FUNCTION,
+ COMMANDTAG_CREATE_INDEX,
+ COMMANDTAG_CREATE_LANGUAGE,
+ COMMANDTAG_CREATE_MATERIALIZED_VIEW,
+ COMMANDTAG_CREATE_OPERATOR,
+ COMMANDTAG_CREATE_OPERATOR_CLASS,
+ COMMANDTAG_CREATE_OPERATOR_FAMILY,
+ COMMANDTAG_CREATE_POLICY,
+ COMMANDTAG_CREATE_PROCEDURE,
+ COMMANDTAG_CREATE_PUBLICATION,
+ COMMANDTAG_CREATE_ROLE,
+ COMMANDTAG_CREATE_ROUTINE,
+ COMMANDTAG_CREATE_RULE,
+ COMMANDTAG_CREATE_SCHEMA,
+ COMMANDTAG_CREATE_SEQUENCE,
+ COMMANDTAG_CREATE_SERVER,
+ COMMANDTAG_CREATE_STATISTICS,
+ COMMANDTAG_CREATE_SUBSCRIPTION,
+ COMMANDTAG_CREATE_TABLE,
+ COMMANDTAG_CREATE_TABLE_AS,
+ COMMANDTAG_CREATE_TABLESPACE,
+ COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION,
+ COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY,
+ COMMANDTAG_CREATE_TEXT_SEARCH_PARSER,
+ COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE,
+ COMMANDTAG_CREATE_TRANSFORM,
+ COMMANDTAG_CREATE_TRIGGER,
+ COMMANDTAG_CREATE_TYPE,
+ COMMANDTAG_CREATE_USER_MAPPING,
+ COMMANDTAG_CREATE_VIEW,
+ COMMANDTAG_DEALLOCATE,
+ COMMANDTAG_DEALLOCATE_ALL,
+ COMMANDTAG_DECLARE_CURSOR,
+ COMMANDTAG_DELETE,
+ COMMANDTAG_DISCARD,
+ COMMANDTAG_DISCARD_ALL,
+ COMMANDTAG_DISCARD_PLANS,
+ COMMANDTAG_DISCARD_SEQUENCES,
+ COMMANDTAG_DISCARD_TEMP,
+ COMMANDTAG_DO,
+ COMMANDTAG_DROP_ACCESS_METHOD,
+ COMMANDTAG_DROP_AGGREGATE,
+ COMMANDTAG_DROP_CAST,
+ COMMANDTAG_DROP_COLLATION,
+ COMMANDTAG_DROP_CONSTRAINT,
+ COMMANDTAG_DROP_CONVERSION,
+ COMMANDTAG_DROP_DATABASE,
+ COMMANDTAG_DROP_DOMAIN,
+ COMMANDTAG_DROP_EVENT_TRIGGER,
+ COMMANDTAG_DROP_EXTENSION,
+ COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER,
+ COMMANDTAG_DROP_FOREIGN_TABLE,
+ COMMANDTAG_DROP_FUNCTION,
+ COMMANDTAG_DROP_INDEX,
+ COMMANDTAG_DROP_LANGUAGE,
+ COMMANDTAG_DROP_MATERIALIZED_VIEW,
+ COMMANDTAG_DROP_OPERATOR,
+ COMMANDTAG_DROP_OPERATOR_CLASS,
+ COMMANDTAG_DROP_OPERATOR_FAMILY,
+ COMMANDTAG_DROP_OWNED,
+ COMMANDTAG_DROP_POLICY,
+ COMMANDTAG_DROP_PROCEDURE,
+ COMMANDTAG_DROP_PUBLICATION,
+ COMMANDTAG_DROP_REPLICATION_SLOT,
+ COMMANDTAG_DROP_ROLE,
+ COMMANDTAG_DROP_ROUTINE,
+ COMMANDTAG_DROP_RULE,
+ COMMANDTAG_DROP_SCHEMA,
+ COMMANDTAG_DROP_SEQUENCE,
+ COMMANDTAG_DROP_SERVER,
+ COMMANDTAG_DROP_STATISTICS,
+ COMMANDTAG_DROP_SUBSCRIPTION,
+ COMMANDTAG_DROP_TABLE,
+ COMMANDTAG_DROP_TABLESPACE,
+ COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION,
+ COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY,
+ COMMANDTAG_DROP_TEXT_SEARCH_PARSER,
+ COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE,
+ COMMANDTAG_DROP_TRANSFORM,
+ COMMANDTAG_DROP_TRIGGER,
+ COMMANDTAG_DROP_TYPE,
+ COMMANDTAG_DROP_USER_MAPPING,
+ COMMANDTAG_DROP_VIEW,
+ COMMANDTAG_EXECUTE,
+ COMMANDTAG_EXPLAIN,
+ COMMANDTAG_FETCH,
+ COMMANDTAG_GRANT,
+ COMMANDTAG_GRANT_ROLE,
+ COMMANDTAG_IMPORT_FOREIGN_SCHEMA,
+ COMMANDTAG_INSERT,
+ COMMANDTAG_LISTEN,
+ COMMANDTAG_LOAD,
+ COMMANDTAG_LOCK_TABLE,
+ COMMANDTAG_MOVE,
+ COMMANDTAG_NOTIFY,
+ COMMANDTAG_PREPARE,
+ COMMANDTAG_PREPARE_TRANSACTION,
+ COMMANDTAG_REASSIGN_OWNED,
+ COMMANDTAG_REFRESH_MATERIALIZED_VIEW,
+ COMMANDTAG_REINDEX,
+ COMMANDTAG_RELEASE,
+ COMMANDTAG_RESET,
+ COMMANDTAG_REVOKE,
+ COMMANDTAG_REVOKE_ROLE,
+ COMMANDTAG_ROLLBACK,
+ COMMANDTAG_ROLLBACK_PREPARED,
+ COMMANDTAG_SAVEPOINT,
+ COMMANDTAG_SECURITY_LABEL,
+ COMMANDTAG_SELECT,
+ COMMANDTAG_SELECT_FOR_KEY_SHARE,
+ COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE,
+ COMMANDTAG_SELECT_FOR_SHARE,
+ COMMANDTAG_SELECT_FOR_UPDATE,
+ COMMANDTAG_SELECT_INTO,
+ COMMANDTAG_SET,
+ COMMANDTAG_SET_CONSTRAINTS,
+ COMMANDTAG_SHOW,
+ COMMANDTAG_START_TRANSACTION,
+ COMMANDTAG_TRUNCATE_TABLE,
+ COMMANDTAG_UNLISTEN,
+ COMMANDTAG_UPDATE,
+ COMMANDTAG_VACUUM
+#define LAST_COMMAND_TAG COMMANDTAG_VACUUM
+} CommandTag;
+
+static inline bool
+PG_VALID_COMMANDTAG(CommandTag commandTag)
+{
+ return (commandTag >= FIRST_COMMAND_TAG &&
+ commandTag <= LAST_COMMAND_TAG);
+}
+
+extern const char *GetCommandTagName(CommandTag commandTag);
+extern CommandTag GetCommandTagEnum(const char *tagname);
+
+#ifdef COMMANDTAG_CHECKING
+extern bool CheckCommandTagEnum(void);
+#endif
+
+#endif /* COMMANDTAG_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 62d64aa0a1..577ad55c0f 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -26,6 +26,7 @@
#include <signal.h>
#include "datatype/timestamp.h" /* for TimestampTz */
+#include "common/commandtag.h" /* for CommandTag enum */
#include "pgtime.h" /* for pg_time_t */
@@ -280,9 +281,12 @@ extern void check_stack_depth(void);
extern bool stack_is_too_deep(void);
/* in tcop/utility.c */
-extern void PreventCommandIfReadOnly(const char *cmdname);
-extern void PreventCommandIfParallelMode(const char *cmdname);
-extern void PreventCommandDuringRecovery(const char *cmdname);
+extern void PreventCommandStrIfReadOnly(const char *commandstr);
+extern void PreventCommandStrIfParallelMode(const char *commandstr);
+extern void PreventCommandStrDuringRecovery(const char *commandstr);
+extern void PreventCommandIfReadOnly(CommandTag commandTag);
+extern void PreventCommandIfParallelMode(CommandTag commandTag);
+extern void PreventCommandDuringRecovery(CommandTag commandTag);
/* in utils/misc/guc.c */
extern int trace_recovery_messages;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index da0706add5..091693b696 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -22,6 +22,7 @@
#ifndef PARSENODES_H
#define PARSENODES_H
+#include "common/commandtag.h"
#include "nodes/bitmapset.h"
#include "nodes/lockoptions.h"
#include "nodes/primnodes.h"
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 35bce731a1..57d094cbad 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -67,6 +67,7 @@
#ifndef DEST_H
#define DEST_H
+#include "common/commandtag.h"
#include "executor/tuptable.h"
@@ -134,9 +135,9 @@ extern PGDLLIMPORT DestReceiver *None_Receiver; /* permanent receiver for
/* The primary destination management functions */
-extern void BeginCommand(const char *commandTag, CommandDest dest);
+extern void BeginCommand(CommandTag commandTag, CommandDest dest);
extern DestReceiver *CreateDestReceiver(CommandDest dest);
-extern void EndCommand(const char *commandTag, CommandDest dest);
+extern void EndCommand(const char *completionTag, CommandDest dest);
/* Additional functions that go with destination management, more or less. */
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index a551e08cb8..762556b159 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -92,7 +92,7 @@ extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
extern Query *UtilityContainsQuery(Node *parsetree);
-extern const char *CreateCommandTag(Node *parsetree);
+extern CommandTag CreateCommandTag(Node *parsetree);
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index e48661ebec..04b02c083b 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -95,7 +95,7 @@ typedef struct CachedPlanSource
int magic; /* should equal CACHEDPLANSOURCE_MAGIC */
struct RawStmt *raw_parse_tree; /* output of raw_parser(), or NULL */
const char *query_string; /* source text of query */
- const char *commandTag; /* command tag (a constant!), or NULL */
+ CommandTag commandTag;
Oid *param_types; /* array of parameter type OIDs, or NULL */
int num_params; /* length of param_types array */
ParserSetupHook parserSetup; /* alternative parameter spec method */
@@ -186,10 +186,10 @@ extern void ResetPlanCache(void);
extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern void CompleteCachedPlan(CachedPlanSource *plansource,
List *querytree_list,
MemoryContext querytree_context,
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 0b69433722..a68fa3642b 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -46,6 +46,7 @@
#ifndef PORTAL_H
#define PORTAL_H
+#include "common/commandtag.h"
#include "datatype/timestamp.h"
#include "executor/execdesc.h"
#include "utils/plancache.h"
@@ -132,7 +133,9 @@ typedef struct PortalData
/* The query or queries the portal will execute */
const char *sourceText; /* text of query (as of 8.4, never NULL) */
- const char *commandTag; /* command tag for original query */
+ char completionTag[COMPLETION_TAG_BUFSIZE]; /* completion string for
+ * executed query */
+ CommandTag commandTag; /* command tag for original query */
List *stmts; /* list of PlannedStmts */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
@@ -194,6 +197,12 @@ typedef struct PortalData
bool visible; /* include this portal in pg_cursors? */
} PortalData;
+static inline const char *
+PortalGetCompletionTag(Portal portal)
+{
+ return (portal->completionTag[0] ? portal->completionTag : GetCommandTagName(portal->commandTag));
+}
+
/*
* PortalIsValid
* True iff portal is valid.
@@ -227,7 +236,7 @@ extern Portal GetPortalByName(const char *name);
extern void PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan);
extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 5acf604f63..8a0f31b4b8 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4115,10 +4115,9 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
* tree(s), since those are the result of rewriting and could have
* been transmogrified into something else entirely.
*/
- if (plansource->commandTag &&
- (strcmp(plansource->commandTag, "INSERT") == 0 ||
- strcmp(plansource->commandTag, "UPDATE") == 0 ||
- strcmp(plansource->commandTag, "DELETE") == 0))
+ if (plansource->commandTag == COMMANDTAG_INSERT ||
+ plansource->commandTag == COMMANDTAG_UPDATE ||
+ plansource->commandTag == COMMANDTAG_DELETE)
{
stmt->mod_stmt = true;
break;
diff --git a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
index e1629ec618..d10c465a24 100644
--- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
+++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
@@ -74,7 +74,7 @@ get_command_tag(PG_FUNCTION_ARGS)
if (!cmd->parsetree)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(cstring_to_text(CreateCommandTag(cmd->parsetree)));
+ PG_RETURN_TEXT_P(cstring_to_text(GetCommandTagName(CreateCommandTag(cmd->parsetree))));
}
/*
--
2.21.1 (Apple Git-122.3)
v1-0002-Extending-the-event_trigger-regression-test.patchapplication/octet-stream; name=v1-0002-Extending-the-event_trigger-regression-test.patch; x-unix-mode=0644Download
From 0ff5ec98b257b2d96f28d7c92b75140fffa6cf3f Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Sun, 2 Feb 2020 15:42:50 -0800
Subject: [PATCH v1 2/2] Extending the event_trigger regression test.
This commit adds coverage for all command tags being used
as tags within CREATE EVENT TRIGGER. The previous coverage
was incomplete.
---
src/test/regress/expected/event_trigger.out | 765 +++++++++++++++++++
src/test/regress/sql/event_trigger.sql | 784 ++++++++++++++++++++
2 files changed, 1549 insertions(+)
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 788be86b33..9e6c845540 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -9,6 +9,12 @@ BEGIN
RAISE NOTICE 'test_event_trigger: % %', tg_event, tg_tag;
END
$$ language plpgsql;
+-- OK
+create function test_event_trigger2() returns event_trigger as $$
+BEGIN
+ RAISE NOTICE 'test_event_trigger2: % %', tg_event, tg_tag;
+END
+$$ LANGUAGE plpgsql;
-- should fail, event triggers cannot have declared arguments
create function test_event_trigger_arg(name text)
returns event_trigger as $$ BEGIN RETURN 1; END $$ language plpgsql;
@@ -80,6 +86,765 @@ create event trigger regress_event_trigger2 on ddl_command_start
execute procedure test_event_trigger();
-- OK
comment on event trigger regress_event_trigger is 'test comment';
+-- These are all unsupported
+create event trigger regress_event_triger_NULL on ddl_command_start
+ when tag in ('')
+ execute procedure test_event_trigger2();
+ERROR: filter value "" not recognized for filter variable "tag"
+create event trigger regress_event_triger_UNKNOWN on ddl_command_start
+ when tag in ('???')
+ execute procedure test_event_trigger2();
+ERROR: filter value "???" not recognized for filter variable "tag"
+create event trigger regress_event_trigger_ALTER_DATABASE on ddl_command_start
+ when tag in ('ALTER DATABASE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ALTER DATABASE
+create event trigger regress_event_trigger_ALTER_EVENT_TRIGGER on ddl_command_start
+ when tag in ('ALTER EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ALTER EVENT TRIGGER
+create event trigger regress_event_trigger_ALTER_ROLE on ddl_command_start
+ when tag in ('ALTER ROLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ALTER ROLE
+create event trigger regress_event_trigger_ALTER_SYSTEM on ddl_command_start
+ when tag in ('ALTER SYSTEM')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ALTER SYSTEM
+create event trigger regress_event_trigger_ALTER_TABLESPACE on ddl_command_start
+ when tag in ('ALTER TABLESPACE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ALTER TABLESPACE
+create event trigger regress_event_trigger_ANALYZE on ddl_command_start
+ when tag in ('ANALYZE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ANALYZE
+create event trigger regress_event_trigger_BEGIN on ddl_command_start
+ when tag in ('BEGIN')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for BEGIN
+create event trigger regress_event_trigger_CALL on ddl_command_start
+ when tag in ('CALL')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CALL
+create event trigger regress_event_trigger_CHECKPOINT on ddl_command_start
+ when tag in ('CHECKPOINT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CHECKPOINT
+create event trigger regress_event_trigger_CLOSE on ddl_command_start
+ when tag in ('CLOSE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CLOSE
+create event trigger regress_event_trigger_CLOSE_CURSOR on ddl_command_start
+ when tag in ('CLOSE CURSOR')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CLOSE CURSOR
+create event trigger regress_event_trigger_CLOSE_CURSOR_ALL on ddl_command_start
+ when tag in ('CLOSE CURSOR ALL')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CLOSE CURSOR ALL
+create event trigger regress_event_trigger_CLUSTER on ddl_command_start
+ when tag in ('CLUSTER')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CLUSTER
+create event trigger regress_event_trigger_COMMIT on ddl_command_start
+ when tag in ('COMMIT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for COMMIT
+create event trigger regress_event_trigger_COMMIT_PREPARED on ddl_command_start
+ when tag in ('COMMIT PREPARED')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for COMMIT PREPARED
+create event trigger regress_event_trigger_COPY on ddl_command_start
+ when tag in ('COPY')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for COPY
+create event trigger regress_event_trigger_COPY_FROM on ddl_command_start
+ when tag in ('COPY FROM')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for COPY FROM
+create event trigger regress_event_trigger_CREATE_DATABASE on ddl_command_start
+ when tag in ('CREATE DATABASE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CREATE DATABASE
+create event trigger regress_event_trigger_CREATE_EVENT_TRIGGER on ddl_command_start
+ when tag in ('CREATE EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CREATE EVENT TRIGGER
+create event trigger regress_event_trigger_CREATE_ROLE on ddl_command_start
+ when tag in ('CREATE ROLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CREATE ROLE
+create event trigger regress_event_trigger_CREATE_TABLESPACE on ddl_command_start
+ when tag in ('CREATE TABLESPACE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CREATE TABLESPACE
+create event trigger regress_event_trigger_DEALLOCATE on ddl_command_start
+ when tag in ('DEALLOCATE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DEALLOCATE
+create event trigger regress_event_trigger_DEALLOCATE_ALL on ddl_command_start
+ when tag in ('DEALLOCATE ALL')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DEALLOCATE ALL
+create event trigger regress_event_trigger_DECLARE_CURSOR on ddl_command_start
+ when tag in ('DECLARE CURSOR')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DECLARE CURSOR
+create event trigger regress_event_trigger_DELETE on ddl_command_start
+ when tag in ('DELETE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DELETE
+create event trigger regress_event_trigger_DISCARD on ddl_command_start
+ when tag in ('DISCARD')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DISCARD
+create event trigger regress_event_trigger_DISCARD_ALL on ddl_command_start
+ when tag in ('DISCARD ALL')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DISCARD ALL
+create event trigger regress_event_trigger_DISCARD_PLANS on ddl_command_start
+ when tag in ('DISCARD PLANS')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DISCARD PLANS
+create event trigger regress_event_trigger_DISCARD_SEQUENCES on ddl_command_start
+ when tag in ('DISCARD SEQUENCES')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DISCARD SEQUENCES
+create event trigger regress_event_trigger_DISCARD_TEMP on ddl_command_start
+ when tag in ('DISCARD TEMP')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DISCARD TEMP
+create event trigger regress_event_trigger_DO on ddl_command_start
+ when tag in ('DO')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DO
+create event trigger regress_event_trigger_DROP_DATABASE on ddl_command_start
+ when tag in ('DROP DATABASE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DROP DATABASE
+create event trigger regress_event_trigger_DROP_EVENT_TRIGGER on ddl_command_start
+ when tag in ('DROP EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DROP EVENT TRIGGER
+create event trigger regress_event_trigger_DROP_REPLICATION_SLOT on ddl_command_start
+ when tag in ('DROP REPLICATION SLOT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DROP REPLICATION SLOT
+create event trigger regress_event_trigger_DROP_ROLE on ddl_command_start
+ when tag in ('DROP ROLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DROP ROLE
+create event trigger regress_event_trigger_DROP_TABLESPACE on ddl_command_start
+ when tag in ('DROP TABLESPACE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DROP TABLESPACE
+create event trigger regress_event_trigger_EXECUTE on ddl_command_start
+ when tag in ('EXECUTE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for EXECUTE
+create event trigger regress_event_trigger_EXPLAIN on ddl_command_start
+ when tag in ('EXPLAIN')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for EXPLAIN
+create event trigger regress_event_trigger_FETCH on ddl_command_start
+ when tag in ('FETCH')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for FETCH
+create event trigger regress_event_trigger_GRANT_ROLE on ddl_command_start
+ when tag in ('GRANT ROLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for GRANT ROLE
+create event trigger regress_event_trigger_INSERT on ddl_command_start
+ when tag in ('INSERT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for INSERT
+create event trigger regress_event_trigger_LISTEN on ddl_command_start
+ when tag in ('LISTEN')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for LISTEN
+create event trigger regress_event_trigger_LOAD on ddl_command_start
+ when tag in ('LOAD')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for LOAD
+create event trigger regress_event_trigger_LOCK_TABLE on ddl_command_start
+ when tag in ('LOCK TABLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for LOCK TABLE
+create event trigger regress_event_trigger_MOVE on ddl_command_start
+ when tag in ('MOVE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for MOVE
+create event trigger regress_event_trigger_NOTIFY on ddl_command_start
+ when tag in ('NOTIFY')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for NOTIFY
+create event trigger regress_event_trigger_PREPARE on ddl_command_start
+ when tag in ('PREPARE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for PREPARE
+create event trigger regress_event_trigger_PREPARE_TRANSACTION on ddl_command_start
+ when tag in ('PREPARE TRANSACTION')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for PREPARE TRANSACTION
+create event trigger regress_event_trigger_REASSIGN_OWNED on ddl_command_start
+ when tag in ('REASSIGN OWNED')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for REASSIGN OWNED
+create event trigger regress_event_trigger_REINDEX on ddl_command_start
+ when tag in ('REINDEX')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for REINDEX
+create event trigger regress_event_trigger_RELEASE on ddl_command_start
+ when tag in ('RELEASE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for RELEASE
+create event trigger regress_event_trigger_RESET on ddl_command_start
+ when tag in ('RESET')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for RESET
+create event trigger regress_event_trigger_REVOKE on ddl_command_start
+ when tag in ('REVOKE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_REVOKE_ROLE on ddl_command_start
+ when tag in ('REVOKE ROLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for REVOKE ROLE
+create event trigger regress_event_trigger_ROLLBACK on ddl_command_start
+ when tag in ('ROLLBACK')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ROLLBACK
+create event trigger regress_event_trigger_ROLLBACK_PREPARED on ddl_command_start
+ when tag in ('ROLLBACK PREPARED')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ROLLBACK PREPARED
+create event trigger regress_event_trigger_SAVEPOINT on ddl_command_start
+ when tag in ('SAVEPOINT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SAVEPOINT
+create event trigger regress_event_trigger_SECURITY_LABEL on ddl_command_start
+ when tag in ('SECURITY LABEL')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_SELECT on ddl_command_start
+ when tag in ('SELECT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SELECT
+create event trigger regress_event_trigger_SELECT_FOR_KEY_SHARE on ddl_command_start
+ when tag in ('SELECT FOR KEY SHARE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SELECT FOR KEY SHARE
+create event trigger regress_event_trigger_SELECT_FOR_NO_KEY_UPDATE on ddl_command_start
+ when tag in ('SELECT FOR NO KEY UPDATE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SELECT FOR NO KEY UPDATE
+create event trigger regress_event_trigger_SELECT_FOR_SHARE on ddl_command_start
+ when tag in ('SELECT FOR SHARE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SELECT FOR SHARE
+create event trigger regress_event_trigger_SELECT_FOR_UPDATE on ddl_command_start
+ when tag in ('SELECT FOR UPDATE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SELECT FOR UPDATE
+create event trigger regress_event_trigger_SET on ddl_command_start
+ when tag in ('SET')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SET
+create event trigger regress_event_trigger_SET_CONSTRAINTS on ddl_command_start
+ when tag in ('SET CONSTRAINTS')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SET CONSTRAINTS
+create event trigger regress_event_trigger_SHOW on ddl_command_start
+ when tag in ('SHOW')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SHOW
+create event trigger regress_event_trigger_START_TRANSACTION on ddl_command_start
+ when tag in ('START TRANSACTION')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for START TRANSACTION
+create event trigger regress_event_trigger_TRUNCATE_TABLE on ddl_command_start
+ when tag in ('TRUNCATE TABLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for TRUNCATE TABLE
+create event trigger regress_event_trigger_UNLISTEN on ddl_command_start
+ when tag in ('UNLISTEN')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for UNLISTEN
+create event trigger regress_event_trigger_UPDATE on ddl_command_start
+ when tag in ('UPDATE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for UPDATE
+create event trigger regress_event_trigger_VACUUM on ddl_command_start
+ when tag in ('VACUUM')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for VACUUM
+-- These are all ok
+create event trigger regress_event_trigger_ALTER_ACCESS_METHOD on ddl_command_start
+ when tag in ('ALTER ACCESS METHOD')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_AGGREGATE on ddl_command_start
+ when tag in ('ALTER AGGREGATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_CAST on ddl_command_start
+ when tag in ('ALTER CAST')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_COLLATION on ddl_command_start
+ when tag in ('ALTER COLLATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_CONSTRAINT on ddl_command_start
+ when tag in ('ALTER CONSTRAINT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_CONVERSION on ddl_command_start
+ when tag in ('ALTER CONVERSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_DEFAULT_PRIVILEGES on ddl_command_start
+ when tag in ('ALTER DEFAULT PRIVILEGES')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_DOMAIN on ddl_command_start
+ when tag in ('ALTER DOMAIN')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_EXTENSION on ddl_command_start
+ when tag in ('ALTER EXTENSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('ALTER FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_FOREIGN_TABLE on ddl_command_start
+ when tag in ('ALTER FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_FUNCTION on ddl_command_start
+ when tag in ('ALTER FUNCTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_INDEX on ddl_command_start
+ when tag in ('ALTER INDEX')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_LANGUAGE on ddl_command_start
+ when tag in ('ALTER LANGUAGE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_LARGE_OBJECT on ddl_command_start
+ when tag in ('ALTER LARGE OBJECT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('ALTER MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_OPERATOR on ddl_command_start
+ when tag in ('ALTER OPERATOR')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_OPERATOR_CLASS on ddl_command_start
+ when tag in ('ALTER OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('ALTER OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_POLICY on ddl_command_start
+ when tag in ('ALTER POLICY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_PROCEDURE on ddl_command_start
+ when tag in ('ALTER PROCEDURE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_PUBLICATION on ddl_command_start
+ when tag in ('ALTER PUBLICATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_ROUTINE on ddl_command_start
+ when tag in ('ALTER ROUTINE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_RULE on ddl_command_start
+ when tag in ('ALTER RULE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_SCHEMA on ddl_command_start
+ when tag in ('ALTER SCHEMA')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_SEQUENCE on ddl_command_start
+ when tag in ('ALTER SEQUENCE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_SERVER on ddl_command_start
+ when tag in ('ALTER SERVER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_STATISTICS on ddl_command_start
+ when tag in ('ALTER STATISTICS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_SUBSCRIPTION on ddl_command_start
+ when tag in ('ALTER SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TABLE on ddl_command_start
+ when tag in ('ALTER TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TRANSFORM on ddl_command_start
+ when tag in ('ALTER TRANSFORM')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TRIGGER on ddl_command_start
+ when tag in ('ALTER TRIGGER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TYPE on ddl_command_start
+ when tag in ('ALTER TYPE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_USER_MAPPING on ddl_command_start
+ when tag in ('ALTER USER MAPPING')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_VIEW on ddl_command_start
+ when tag in ('ALTER VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_COMMENT on ddl_command_start
+ when tag in ('COMMENT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_ACCESS_METHOD on ddl_command_start
+ when tag in ('CREATE ACCESS METHOD')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_AGGREGATE on ddl_command_start
+ when tag in ('CREATE AGGREGATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_CAST on ddl_command_start
+ when tag in ('CREATE CAST')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_COLLATION on ddl_command_start
+ when tag in ('CREATE COLLATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_CONSTRAINT on ddl_command_start
+ when tag in ('CREATE CONSTRAINT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_CONVERSION on ddl_command_start
+ when tag in ('CREATE CONVERSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_DOMAIN on ddl_command_start
+ when tag in ('CREATE DOMAIN')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_EXTENSION on ddl_command_start
+ when tag in ('CREATE EXTENSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('CREATE FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_FOREIGN_TABLE on ddl_command_start
+ when tag in ('CREATE FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_FUNCTION on ddl_command_start
+ when tag in ('CREATE FUNCTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_INDEX on ddl_command_start
+ when tag in ('CREATE INDEX')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_LANGUAGE on ddl_command_start
+ when tag in ('CREATE LANGUAGE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('CREATE MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_OPERATOR on ddl_command_start
+ when tag in ('CREATE OPERATOR')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_OPERATOR_CLASS on ddl_command_start
+ when tag in ('CREATE OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('CREATE OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_POLICY on ddl_command_start
+ when tag in ('CREATE POLICY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_PROCEDURE on ddl_command_start
+ when tag in ('CREATE PROCEDURE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_PUBLICATION on ddl_command_start
+ when tag in ('CREATE PUBLICATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_ROUTINE on ddl_command_start
+ when tag in ('CREATE ROUTINE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_RULE on ddl_command_start
+ when tag in ('CREATE RULE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_SCHEMA on ddl_command_start
+ when tag in ('CREATE SCHEMA')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_SEQUENCE on ddl_command_start
+ when tag in ('CREATE SEQUENCE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_SERVER on ddl_command_start
+ when tag in ('CREATE SERVER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_STATISTICS on ddl_command_start
+ when tag in ('CREATE STATISTICS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_SUBSCRIPTION on ddl_command_start
+ when tag in ('CREATE SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TABLE on ddl_command_start
+ when tag in ('CREATE TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TABLE_AS on ddl_command_start
+ when tag in ('CREATE TABLE AS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TRANSFORM on ddl_command_start
+ when tag in ('CREATE TRANSFORM')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TRIGGER on ddl_command_start
+ when tag in ('CREATE TRIGGER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TYPE on ddl_command_start
+ when tag in ('CREATE TYPE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_USER_MAPPING on ddl_command_start
+ when tag in ('CREATE USER MAPPING')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_VIEW on ddl_command_start
+ when tag in ('CREATE VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_ACCESS_METHOD on ddl_command_start
+ when tag in ('DROP ACCESS METHOD')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_AGGREGATE on ddl_command_start
+ when tag in ('DROP AGGREGATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_CAST on ddl_command_start
+ when tag in ('DROP CAST')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_COLLATION on ddl_command_start
+ when tag in ('DROP COLLATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_CONSTRAINT on ddl_command_start
+ when tag in ('DROP CONSTRAINT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_CONVERSION on ddl_command_start
+ when tag in ('DROP CONVERSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_DOMAIN on ddl_command_start
+ when tag in ('DROP DOMAIN')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_EXTENSION on ddl_command_start
+ when tag in ('DROP EXTENSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('DROP FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_FOREIGN_TABLE on ddl_command_start
+ when tag in ('DROP FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_FUNCTION on ddl_command_start
+ when tag in ('DROP FUNCTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_INDEX on ddl_command_start
+ when tag in ('DROP INDEX')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_LANGUAGE on ddl_command_start
+ when tag in ('DROP LANGUAGE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('DROP MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_OPERATOR on ddl_command_start
+ when tag in ('DROP OPERATOR')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_OPERATOR_CLASS on ddl_command_start
+ when tag in ('DROP OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('DROP OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_OWNED on ddl_command_start
+ when tag in ('DROP OWNED')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_POLICY on ddl_command_start
+ when tag in ('DROP POLICY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_PROCEDURE on ddl_command_start
+ when tag in ('DROP PROCEDURE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_PUBLICATION on ddl_command_start
+ when tag in ('DROP PUBLICATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_ROUTINE on ddl_command_start
+ when tag in ('DROP ROUTINE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_RULE on ddl_command_start
+ when tag in ('DROP RULE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_SCHEMA on ddl_command_start
+ when tag in ('DROP SCHEMA')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_SEQUENCE on ddl_command_start
+ when tag in ('DROP SEQUENCE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_SERVER on ddl_command_start
+ when tag in ('DROP SERVER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_STATISTICS on ddl_command_start
+ when tag in ('DROP STATISTICS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_SUBSCRIPTION on ddl_command_start
+ when tag in ('DROP SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TABLE on ddl_command_start
+ when tag in ('DROP TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('DROP TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('DROP TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('DROP TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('DROP TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TRANSFORM on ddl_command_start
+ when tag in ('DROP TRANSFORM')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TRIGGER on ddl_command_start
+ when tag in ('DROP TRIGGER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TYPE on ddl_command_start
+ when tag in ('DROP TYPE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_USER_MAPPING on ddl_command_start
+ when tag in ('DROP USER MAPPING')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_VIEW on ddl_command_start
+ when tag in ('DROP VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_GRANT on ddl_command_start
+ when tag in ('GRANT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_IMPORT_FOREIGN_SCHEMA on ddl_command_start
+ when tag in ('IMPORT FOREIGN SCHEMA')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_REFRESH_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('REFRESH MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_SELECT_INTO on ddl_command_start
+ when tag in ('SELECT INTO')
+ execute procedure test_event_trigger2();
+-- drop should cascade to the event trigger
+drop function test_event_trigger2() cascade;
+NOTICE: test_event_trigger: ddl_command_start DROP FUNCTION
+NOTICE: test_event_trigger2: ddl_command_start DROP FUNCTION
+NOTICE: drop cascades to 122 other objects
+DETAIL: drop cascades to event trigger regress_event_trigger_revoke
+drop cascades to event trigger regress_event_trigger_security_label
+drop cascades to event trigger regress_event_trigger_alter_access_method
+drop cascades to event trigger regress_event_trigger_alter_aggregate
+drop cascades to event trigger regress_event_trigger_alter_cast
+drop cascades to event trigger regress_event_trigger_alter_collation
+drop cascades to event trigger regress_event_trigger_alter_constraint
+drop cascades to event trigger regress_event_trigger_alter_conversion
+drop cascades to event trigger regress_event_trigger_alter_default_privileges
+drop cascades to event trigger regress_event_trigger_alter_domain
+drop cascades to event trigger regress_event_trigger_alter_extension
+drop cascades to event trigger regress_event_trigger_alter_foreign_data_wrapper
+drop cascades to event trigger regress_event_trigger_alter_foreign_table
+drop cascades to event trigger regress_event_trigger_alter_function
+drop cascades to event trigger regress_event_trigger_alter_index
+drop cascades to event trigger regress_event_trigger_alter_language
+drop cascades to event trigger regress_event_trigger_alter_large_object
+drop cascades to event trigger regress_event_trigger_alter_materialized_view
+drop cascades to event trigger regress_event_trigger_alter_operator
+drop cascades to event trigger regress_event_trigger_alter_operator_class
+drop cascades to event trigger regress_event_trigger_alter_operator_family
+drop cascades to event trigger regress_event_trigger_alter_policy
+drop cascades to event trigger regress_event_trigger_alter_procedure
+drop cascades to event trigger regress_event_trigger_alter_publication
+drop cascades to event trigger regress_event_trigger_alter_routine
+drop cascades to event trigger regress_event_trigger_alter_rule
+drop cascades to event trigger regress_event_trigger_alter_schema
+drop cascades to event trigger regress_event_trigger_alter_sequence
+drop cascades to event trigger regress_event_trigger_alter_server
+drop cascades to event trigger regress_event_trigger_alter_statistics
+drop cascades to event trigger regress_event_trigger_alter_subscription
+drop cascades to event trigger regress_event_trigger_alter_table
+drop cascades to event trigger regress_event_trigger_alter_text_search_configuration
+drop cascades to event trigger regress_event_trigger_alter_text_search_dictionary
+drop cascades to event trigger regress_event_trigger_alter_text_search_parser
+drop cascades to event trigger regress_event_trigger_alter_text_search_template
+drop cascades to event trigger regress_event_trigger_alter_transform
+drop cascades to event trigger regress_event_trigger_alter_trigger
+drop cascades to event trigger regress_event_trigger_alter_type
+drop cascades to event trigger regress_event_trigger_alter_user_mapping
+drop cascades to event trigger regress_event_trigger_alter_view
+drop cascades to event trigger regress_event_trigger_comment
+drop cascades to event trigger regress_event_trigger_create_access_method
+drop cascades to event trigger regress_event_trigger_create_aggregate
+drop cascades to event trigger regress_event_trigger_create_cast
+drop cascades to event trigger regress_event_trigger_create_collation
+drop cascades to event trigger regress_event_trigger_create_constraint
+drop cascades to event trigger regress_event_trigger_create_conversion
+drop cascades to event trigger regress_event_trigger_create_domain
+drop cascades to event trigger regress_event_trigger_create_extension
+drop cascades to event trigger regress_event_trigger_create_foreign_data_wrapper
+drop cascades to event trigger regress_event_trigger_create_foreign_table
+drop cascades to event trigger regress_event_trigger_create_function
+drop cascades to event trigger regress_event_trigger_create_index
+drop cascades to event trigger regress_event_trigger_create_language
+drop cascades to event trigger regress_event_trigger_create_materialized_view
+drop cascades to event trigger regress_event_trigger_create_operator
+drop cascades to event trigger regress_event_trigger_create_operator_class
+drop cascades to event trigger regress_event_trigger_create_operator_family
+drop cascades to event trigger regress_event_trigger_create_policy
+drop cascades to event trigger regress_event_trigger_create_procedure
+drop cascades to event trigger regress_event_trigger_create_publication
+drop cascades to event trigger regress_event_trigger_create_routine
+drop cascades to event trigger regress_event_trigger_create_rule
+drop cascades to event trigger regress_event_trigger_create_schema
+drop cascades to event trigger regress_event_trigger_create_sequence
+drop cascades to event trigger regress_event_trigger_create_server
+drop cascades to event trigger regress_event_trigger_create_statistics
+drop cascades to event trigger regress_event_trigger_create_subscription
+drop cascades to event trigger regress_event_trigger_create_table
+drop cascades to event trigger regress_event_trigger_create_table_as
+drop cascades to event trigger regress_event_trigger_create_text_search_configuration
+drop cascades to event trigger regress_event_trigger_create_text_search_dictionary
+drop cascades to event trigger regress_event_trigger_create_text_search_parser
+drop cascades to event trigger regress_event_trigger_create_text_search_template
+drop cascades to event trigger regress_event_trigger_create_transform
+drop cascades to event trigger regress_event_trigger_create_trigger
+drop cascades to event trigger regress_event_trigger_create_type
+drop cascades to event trigger regress_event_trigger_create_user_mapping
+drop cascades to event trigger regress_event_trigger_create_view
+drop cascades to event trigger regress_event_trigger_drop_access_method
+drop cascades to event trigger regress_event_trigger_drop_aggregate
+drop cascades to event trigger regress_event_trigger_drop_cast
+drop cascades to event trigger regress_event_trigger_drop_collation
+drop cascades to event trigger regress_event_trigger_drop_constraint
+drop cascades to event trigger regress_event_trigger_drop_conversion
+drop cascades to event trigger regress_event_trigger_drop_domain
+drop cascades to event trigger regress_event_trigger_drop_extension
+drop cascades to event trigger regress_event_trigger_drop_foreign_data_wrapper
+drop cascades to event trigger regress_event_trigger_drop_foreign_table
+drop cascades to event trigger regress_event_trigger_drop_function
+drop cascades to event trigger regress_event_trigger_drop_index
+drop cascades to event trigger regress_event_trigger_drop_language
+drop cascades to event trigger regress_event_trigger_drop_materialized_view
+drop cascades to event trigger regress_event_trigger_drop_operator
+drop cascades to event trigger regress_event_trigger_drop_operator_class
+drop cascades to event trigger regress_event_trigger_drop_operator_family
+drop cascades to event trigger regress_event_trigger_drop_owned
+drop cascades to event trigger regress_event_trigger_drop_policy
+drop cascades to event trigger regress_event_trigger_drop_procedure
+and 22 other objects (see server log for list)
+NOTICE: test_event_trigger: ddl_command_end DROP FUNCTION
-- drop as non-superuser should fail
create role regress_evt_user;
set role regress_evt_user;
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 346168673d..cad02212ad 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -10,6 +10,13 @@ BEGIN
END
$$ language plpgsql;
+-- OK
+create function test_event_trigger2() returns event_trigger as $$
+BEGIN
+ RAISE NOTICE 'test_event_trigger2: % %', tg_event, tg_tag;
+END
+$$ LANGUAGE plpgsql;
+
-- should fail, event triggers cannot have declared arguments
create function test_event_trigger_arg(name text)
returns event_trigger as $$ BEGIN RETURN 1; END $$ language plpgsql;
@@ -82,6 +89,783 @@ create event trigger regress_event_trigger2 on ddl_command_start
-- OK
comment on event trigger regress_event_trigger is 'test comment';
+-- These are all unsupported
+create event trigger regress_event_triger_NULL on ddl_command_start
+ when tag in ('')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_triger_UNKNOWN on ddl_command_start
+ when tag in ('???')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_DATABASE on ddl_command_start
+ when tag in ('ALTER DATABASE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_EVENT_TRIGGER on ddl_command_start
+ when tag in ('ALTER EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_ROLE on ddl_command_start
+ when tag in ('ALTER ROLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_SYSTEM on ddl_command_start
+ when tag in ('ALTER SYSTEM')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TABLESPACE on ddl_command_start
+ when tag in ('ALTER TABLESPACE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ANALYZE on ddl_command_start
+ when tag in ('ANALYZE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_BEGIN on ddl_command_start
+ when tag in ('BEGIN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CALL on ddl_command_start
+ when tag in ('CALL')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CHECKPOINT on ddl_command_start
+ when tag in ('CHECKPOINT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CLOSE on ddl_command_start
+ when tag in ('CLOSE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CLOSE_CURSOR on ddl_command_start
+ when tag in ('CLOSE CURSOR')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CLOSE_CURSOR_ALL on ddl_command_start
+ when tag in ('CLOSE CURSOR ALL')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CLUSTER on ddl_command_start
+ when tag in ('CLUSTER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_COMMIT on ddl_command_start
+ when tag in ('COMMIT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_COMMIT_PREPARED on ddl_command_start
+ when tag in ('COMMIT PREPARED')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_COPY on ddl_command_start
+ when tag in ('COPY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_COPY_FROM on ddl_command_start
+ when tag in ('COPY FROM')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_DATABASE on ddl_command_start
+ when tag in ('CREATE DATABASE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_EVENT_TRIGGER on ddl_command_start
+ when tag in ('CREATE EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_ROLE on ddl_command_start
+ when tag in ('CREATE ROLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TABLESPACE on ddl_command_start
+ when tag in ('CREATE TABLESPACE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DEALLOCATE on ddl_command_start
+ when tag in ('DEALLOCATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DEALLOCATE_ALL on ddl_command_start
+ when tag in ('DEALLOCATE ALL')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DECLARE_CURSOR on ddl_command_start
+ when tag in ('DECLARE CURSOR')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DELETE on ddl_command_start
+ when tag in ('DELETE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DISCARD on ddl_command_start
+ when tag in ('DISCARD')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DISCARD_ALL on ddl_command_start
+ when tag in ('DISCARD ALL')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DISCARD_PLANS on ddl_command_start
+ when tag in ('DISCARD PLANS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DISCARD_SEQUENCES on ddl_command_start
+ when tag in ('DISCARD SEQUENCES')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DISCARD_TEMP on ddl_command_start
+ when tag in ('DISCARD TEMP')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DO on ddl_command_start
+ when tag in ('DO')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_DATABASE on ddl_command_start
+ when tag in ('DROP DATABASE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_EVENT_TRIGGER on ddl_command_start
+ when tag in ('DROP EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_REPLICATION_SLOT on ddl_command_start
+ when tag in ('DROP REPLICATION SLOT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_ROLE on ddl_command_start
+ when tag in ('DROP ROLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TABLESPACE on ddl_command_start
+ when tag in ('DROP TABLESPACE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_EXECUTE on ddl_command_start
+ when tag in ('EXECUTE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_EXPLAIN on ddl_command_start
+ when tag in ('EXPLAIN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_FETCH on ddl_command_start
+ when tag in ('FETCH')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_GRANT_ROLE on ddl_command_start
+ when tag in ('GRANT ROLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_INSERT on ddl_command_start
+ when tag in ('INSERT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_LISTEN on ddl_command_start
+ when tag in ('LISTEN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_LOAD on ddl_command_start
+ when tag in ('LOAD')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_LOCK_TABLE on ddl_command_start
+ when tag in ('LOCK TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_MOVE on ddl_command_start
+ when tag in ('MOVE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_NOTIFY on ddl_command_start
+ when tag in ('NOTIFY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_PREPARE on ddl_command_start
+ when tag in ('PREPARE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_PREPARE_TRANSACTION on ddl_command_start
+ when tag in ('PREPARE TRANSACTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_REASSIGN_OWNED on ddl_command_start
+ when tag in ('REASSIGN OWNED')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_REINDEX on ddl_command_start
+ when tag in ('REINDEX')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_RELEASE on ddl_command_start
+ when tag in ('RELEASE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_RESET on ddl_command_start
+ when tag in ('RESET')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_REVOKE on ddl_command_start
+ when tag in ('REVOKE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_REVOKE_ROLE on ddl_command_start
+ when tag in ('REVOKE ROLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ROLLBACK on ddl_command_start
+ when tag in ('ROLLBACK')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ROLLBACK_PREPARED on ddl_command_start
+ when tag in ('ROLLBACK PREPARED')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SAVEPOINT on ddl_command_start
+ when tag in ('SAVEPOINT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SECURITY_LABEL on ddl_command_start
+ when tag in ('SECURITY LABEL')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT on ddl_command_start
+ when tag in ('SELECT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT_FOR_KEY_SHARE on ddl_command_start
+ when tag in ('SELECT FOR KEY SHARE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT_FOR_NO_KEY_UPDATE on ddl_command_start
+ when tag in ('SELECT FOR NO KEY UPDATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT_FOR_SHARE on ddl_command_start
+ when tag in ('SELECT FOR SHARE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT_FOR_UPDATE on ddl_command_start
+ when tag in ('SELECT FOR UPDATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SET on ddl_command_start
+ when tag in ('SET')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SET_CONSTRAINTS on ddl_command_start
+ when tag in ('SET CONSTRAINTS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SHOW on ddl_command_start
+ when tag in ('SHOW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_START_TRANSACTION on ddl_command_start
+ when tag in ('START TRANSACTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_TRUNCATE_TABLE on ddl_command_start
+ when tag in ('TRUNCATE TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_UNLISTEN on ddl_command_start
+ when tag in ('UNLISTEN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_UPDATE on ddl_command_start
+ when tag in ('UPDATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_VACUUM on ddl_command_start
+ when tag in ('VACUUM')
+ execute procedure test_event_trigger2();
+
+-- These are all ok
+create event trigger regress_event_trigger_ALTER_ACCESS_METHOD on ddl_command_start
+ when tag in ('ALTER ACCESS METHOD')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_AGGREGATE on ddl_command_start
+ when tag in ('ALTER AGGREGATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_CAST on ddl_command_start
+ when tag in ('ALTER CAST')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_COLLATION on ddl_command_start
+ when tag in ('ALTER COLLATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_CONSTRAINT on ddl_command_start
+ when tag in ('ALTER CONSTRAINT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_CONVERSION on ddl_command_start
+ when tag in ('ALTER CONVERSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_DEFAULT_PRIVILEGES on ddl_command_start
+ when tag in ('ALTER DEFAULT PRIVILEGES')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_DOMAIN on ddl_command_start
+ when tag in ('ALTER DOMAIN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_EXTENSION on ddl_command_start
+ when tag in ('ALTER EXTENSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('ALTER FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_FOREIGN_TABLE on ddl_command_start
+ when tag in ('ALTER FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_FUNCTION on ddl_command_start
+ when tag in ('ALTER FUNCTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_INDEX on ddl_command_start
+ when tag in ('ALTER INDEX')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_LANGUAGE on ddl_command_start
+ when tag in ('ALTER LANGUAGE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_LARGE_OBJECT on ddl_command_start
+ when tag in ('ALTER LARGE OBJECT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('ALTER MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_OPERATOR on ddl_command_start
+ when tag in ('ALTER OPERATOR')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_OPERATOR_CLASS on ddl_command_start
+ when tag in ('ALTER OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('ALTER OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_POLICY on ddl_command_start
+ when tag in ('ALTER POLICY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_PROCEDURE on ddl_command_start
+ when tag in ('ALTER PROCEDURE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_PUBLICATION on ddl_command_start
+ when tag in ('ALTER PUBLICATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_ROUTINE on ddl_command_start
+ when tag in ('ALTER ROUTINE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_RULE on ddl_command_start
+ when tag in ('ALTER RULE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_SCHEMA on ddl_command_start
+ when tag in ('ALTER SCHEMA')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_SEQUENCE on ddl_command_start
+ when tag in ('ALTER SEQUENCE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_SERVER on ddl_command_start
+ when tag in ('ALTER SERVER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_STATISTICS on ddl_command_start
+ when tag in ('ALTER STATISTICS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_SUBSCRIPTION on ddl_command_start
+ when tag in ('ALTER SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TABLE on ddl_command_start
+ when tag in ('ALTER TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TRANSFORM on ddl_command_start
+ when tag in ('ALTER TRANSFORM')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TRIGGER on ddl_command_start
+ when tag in ('ALTER TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TYPE on ddl_command_start
+ when tag in ('ALTER TYPE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_USER_MAPPING on ddl_command_start
+ when tag in ('ALTER USER MAPPING')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_VIEW on ddl_command_start
+ when tag in ('ALTER VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_COMMENT on ddl_command_start
+ when tag in ('COMMENT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_ACCESS_METHOD on ddl_command_start
+ when tag in ('CREATE ACCESS METHOD')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_AGGREGATE on ddl_command_start
+ when tag in ('CREATE AGGREGATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_CAST on ddl_command_start
+ when tag in ('CREATE CAST')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_COLLATION on ddl_command_start
+ when tag in ('CREATE COLLATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_CONSTRAINT on ddl_command_start
+ when tag in ('CREATE CONSTRAINT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_CONVERSION on ddl_command_start
+ when tag in ('CREATE CONVERSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_DOMAIN on ddl_command_start
+ when tag in ('CREATE DOMAIN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_EXTENSION on ddl_command_start
+ when tag in ('CREATE EXTENSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('CREATE FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_FOREIGN_TABLE on ddl_command_start
+ when tag in ('CREATE FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_FUNCTION on ddl_command_start
+ when tag in ('CREATE FUNCTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_INDEX on ddl_command_start
+ when tag in ('CREATE INDEX')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_LANGUAGE on ddl_command_start
+ when tag in ('CREATE LANGUAGE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('CREATE MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_OPERATOR on ddl_command_start
+ when tag in ('CREATE OPERATOR')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_OPERATOR_CLASS on ddl_command_start
+ when tag in ('CREATE OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('CREATE OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_POLICY on ddl_command_start
+ when tag in ('CREATE POLICY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_PROCEDURE on ddl_command_start
+ when tag in ('CREATE PROCEDURE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_PUBLICATION on ddl_command_start
+ when tag in ('CREATE PUBLICATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_ROUTINE on ddl_command_start
+ when tag in ('CREATE ROUTINE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_RULE on ddl_command_start
+ when tag in ('CREATE RULE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_SCHEMA on ddl_command_start
+ when tag in ('CREATE SCHEMA')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_SEQUENCE on ddl_command_start
+ when tag in ('CREATE SEQUENCE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_SERVER on ddl_command_start
+ when tag in ('CREATE SERVER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_STATISTICS on ddl_command_start
+ when tag in ('CREATE STATISTICS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_SUBSCRIPTION on ddl_command_start
+ when tag in ('CREATE SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TABLE on ddl_command_start
+ when tag in ('CREATE TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TABLE_AS on ddl_command_start
+ when tag in ('CREATE TABLE AS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TRANSFORM on ddl_command_start
+ when tag in ('CREATE TRANSFORM')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TRIGGER on ddl_command_start
+ when tag in ('CREATE TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TYPE on ddl_command_start
+ when tag in ('CREATE TYPE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_USER_MAPPING on ddl_command_start
+ when tag in ('CREATE USER MAPPING')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_VIEW on ddl_command_start
+ when tag in ('CREATE VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_ACCESS_METHOD on ddl_command_start
+ when tag in ('DROP ACCESS METHOD')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_AGGREGATE on ddl_command_start
+ when tag in ('DROP AGGREGATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_CAST on ddl_command_start
+ when tag in ('DROP CAST')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_COLLATION on ddl_command_start
+ when tag in ('DROP COLLATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_CONSTRAINT on ddl_command_start
+ when tag in ('DROP CONSTRAINT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_CONVERSION on ddl_command_start
+ when tag in ('DROP CONVERSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_DOMAIN on ddl_command_start
+ when tag in ('DROP DOMAIN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_EXTENSION on ddl_command_start
+ when tag in ('DROP EXTENSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('DROP FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_FOREIGN_TABLE on ddl_command_start
+ when tag in ('DROP FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_FUNCTION on ddl_command_start
+ when tag in ('DROP FUNCTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_INDEX on ddl_command_start
+ when tag in ('DROP INDEX')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_LANGUAGE on ddl_command_start
+ when tag in ('DROP LANGUAGE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('DROP MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_OPERATOR on ddl_command_start
+ when tag in ('DROP OPERATOR')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_OPERATOR_CLASS on ddl_command_start
+ when tag in ('DROP OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('DROP OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_OWNED on ddl_command_start
+ when tag in ('DROP OWNED')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_POLICY on ddl_command_start
+ when tag in ('DROP POLICY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_PROCEDURE on ddl_command_start
+ when tag in ('DROP PROCEDURE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_PUBLICATION on ddl_command_start
+ when tag in ('DROP PUBLICATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_ROUTINE on ddl_command_start
+ when tag in ('DROP ROUTINE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_RULE on ddl_command_start
+ when tag in ('DROP RULE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_SCHEMA on ddl_command_start
+ when tag in ('DROP SCHEMA')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_SEQUENCE on ddl_command_start
+ when tag in ('DROP SEQUENCE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_SERVER on ddl_command_start
+ when tag in ('DROP SERVER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_STATISTICS on ddl_command_start
+ when tag in ('DROP STATISTICS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_SUBSCRIPTION on ddl_command_start
+ when tag in ('DROP SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TABLE on ddl_command_start
+ when tag in ('DROP TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('DROP TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('DROP TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('DROP TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('DROP TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TRANSFORM on ddl_command_start
+ when tag in ('DROP TRANSFORM')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TRIGGER on ddl_command_start
+ when tag in ('DROP TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TYPE on ddl_command_start
+ when tag in ('DROP TYPE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_USER_MAPPING on ddl_command_start
+ when tag in ('DROP USER MAPPING')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_VIEW on ddl_command_start
+ when tag in ('DROP VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_GRANT on ddl_command_start
+ when tag in ('GRANT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_IMPORT_FOREIGN_SCHEMA on ddl_command_start
+ when tag in ('IMPORT FOREIGN SCHEMA')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_REFRESH_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('REFRESH MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT_INTO on ddl_command_start
+ when tag in ('SELECT INTO')
+ execute procedure test_event_trigger2();
+
+-- drop should cascade to the event trigger
+drop function test_event_trigger2() cascade;
+
-- drop as non-superuser should fail
create role regress_evt_user;
set role regress_evt_user;
--
2.21.1 (Apple Git-122.3)
Mark Dilger <mark.dilger@enterprisedb.com> writes:
I put the CommandTag enum in src/common because there wasn’t any reason
not to do so. It seems plausible that frontend test frameworks might want
access to this enum.
Au contraire, that's an absolutely fundamental mistake. There is
zero chance of this enum holding still across PG versions, so if
we expose it to frontend code, we're going to have big problems
with cross-version compatibility. See our historical problems with
assuming the enum for character set encodings was the same between
frontend and backend ... and that set is orders of magnitude more
stable than this one.
As I recall, the hardest problem with de-string-ifying this is the fact
that for certain tags we include a rowcount in the string. I'd like to
see that undone --- we have to keep it like that on-the-wire to avoid a
protocol break, but it'd be best if noplace inside the backend did it that
way, and we converted at the last moment before sending a CommandComplete
to the client. Your references to "completion tags" make it sound like
you've only half done the conversion (though I've not read the patch
in enough detail to verify).
BTW, the size of the patch is rather depressing, especially if it's
only half done. Unlike Andres, I'm not even a little bit convinced
that this is worth the amount of code churn it'll cause. Have you
done any code-size or speed comparisons?
regards, tom lane
On Feb 2, 2020, at 6:14 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Mark Dilger <mark.dilger@enterprisedb.com> writes:
I put the CommandTag enum in src/common because there wasn’t any reason
not to do so. It seems plausible that frontend test frameworks might want
access to this enum.
Thanks for looking!
Au contraire, that's an absolutely fundamental mistake. There is
zero chance of this enum holding still across PG versions, so if
we expose it to frontend code, we're going to have big problems
with cross-version compatibility. See our historical problems with
assuming the enum for character set encodings was the same between
frontend and backend ... and that set is orders of magnitude more
stable than this one.
I completely agree that this enum cannot be expected to remain stable across versions.
For the purposes of this patch, which has nothing to do with frontend tools, this issue doesn’t matter to me. I’m happy to move this into src/backend.
Is there no place to put code which would be useful for frontend tools without implying stability? Sure, psql and friends can’t use it, because they need to be able to connect to servers of other versions. But why couldn’t a test framework tool use something like this? Could we have someplace like src/common/volatile for this sort of thing?
As I recall, the hardest problem with de-string-ifying this is the fact
that for certain tags we include a rowcount in the string. I'd like to
see that undone --- we have to keep it like that on-the-wire to avoid a
protocol break, but it'd be best if noplace inside the backend did it that
way, and we converted at the last moment before sending a CommandComplete
to the client. Your references to "completion tags" make it sound like
you've only half done the conversion (though I've not read the patch
in enough detail to verify).
In v1, I stayed closer to the existing code structure than you are requesting. I like the direction you’re suggesting that I go, and I’ve begun that transition in anticipation of posting a v2 patch set soon.
BTW, the size of the patch is rather depressing, especially if it's
only half done. Unlike Andres, I'm not even a little bit convinced
that this is worth the amount of code churn it'll cause. Have you
done any code-size or speed comparisons?
A fair amount of the code churn is replacing strings with their enum equivalent, creating the enum itself, and creating a data table mapping enums to strings. The churn doesn’t look too bad to me when viewing the original vs new code diff side-by-side.
The second file (v1-0002…) is entirely an extension of the regression tests. Applying v1-0001… doesn’t entail needing to apply v1-0002… as the code being tested exists before and after the patch. If you don’t want to apply that regression test change, that’s fine. It just provides more extensive coverage of event_triggers over different command tags.
There will be a bit more churn in v2, since I’m changing the code flow a bit more to avoid generating the strings until they are about to get sent to the client, per your comments above. That has the advantage that multiple places in the old code where the completionTag was parsed to get the nprocessed count back out now doesn’t need any parsing.
I’ll include stats about code-size and speed when I post v2.
Thanks again for reviewing my patch idea!
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Feb 3, 2020, at 9:41 AM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
In v1, I stayed closer to the existing code structure than you are requesting. I like the direction you’re suggesting that I go, and I’ve begun that transition in anticipation of posting a v2 patch set soon.
Ok, here is v2, attached.
In master, a number of functions pass a char *completionTag argument (really a char completionTag[COMPLETION_TAG_BUFSIZE]) which gets filled in with the string to return to the client from EndCommand. I have removed that kind of logic:
- /* save the rowcount if we're given a completionTag to fill */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
In the patch, this is replaced with a new struct, QueryCompletionData. That bit of code above is replaced with:
+ /* save the rowcount if we're given a qc to fill */
+ if (qc)
+ SetQC(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed, DISPLAYFORMAT_NPROCESSED);
For wire protocol compatibility, we have to track the display format. When this gets to EndCommand, the display format allows the string to be written exactly as the client will expect. If we ever get to the point where we can break with that compatibility, the third member of this struct, display_format, can be removed.
Where string parsing is being done in master to get the count back out, it changes to look like this:
- if (strncmp(completionTag, "SELECT ", 7) == 0)
- _SPI_current->processed =
- pg_strtouint64(completionTag + 7, NULL, 10);
+ if (qcdata.commandTag == COMMANDTAG_SELECT)
+ _SPI_current->processed = qcdata.nprocessed;
One of the advantages to the patch is that the commandTag for a portal is not overwritten by the commandTag in the QueryCompletionData, meaning for example that if an EXECUTE command returns the string “UPDATE 0”, the portal->commandTag remains COMMANDTAG_EXECUTE while the qcdata.commandTag becomes COMMANDTAG_UPDATE. This could be helpful to code trying to track how many operations of a given type have run.
In event_trigger.c, in master there are ad-hoc comparisons against c-strings:
- /*
- * Handle some idiosyncratic special cases.
- */
- if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
- pg_strcasecmp(tag, "SELECT INTO") == 0 ||
- pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
- pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
- pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
- pg_strcasecmp(tag, "COMMENT") == 0 ||
- pg_strcasecmp(tag, "GRANT") == 0 ||
- pg_strcasecmp(tag, "REVOKE") == 0 ||
- pg_strcasecmp(tag, "DROP OWNED") == 0 ||
- pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
These are replaced by switch() case statements over the possible commandTags:
+ switch (commandTag)
+ {
+ /*
+ * Supported idiosyncratic special cases.
+ */
+ case COMMANDTAG_ALTER_DEFAULT_PRIVILEGES:
+ case COMMANDTAG_ALTER_LARGE_OBJECT:
+ case COMMANDTAG_COMMENT:
+ case COMMANDTAG_CREATE_TABLE_AS:
+ case COMMANDTAG_DROP_OWNED:
+ case COMMANDTAG_GRANT:
+ case COMMANDTAG_IMPORT_FOREIGN_SCHEMA:
+ case COMMANDTAG_REFRESH_MATERIALIZED_VIEW:
+ case COMMANDTAG_REVOKE:
+ case COMMANDTAG_SECURITY_LABEL:
+ case COMMANDTAG_SELECT_INTO:
I think this is easier to read, verify, and maintain. The compiler can help if you leave a command tag out of the list, which the compiler cannot help discover in master as it is currently written. But I also think all those pg_strcasecmp calls are likely more expensive at runtime.
In master, EventTriggerCacheItem tracks a sorted array of palloc’d cstrings. In the patch, that becomes a Bitmapset over the enum:
typedef struct
{
Oid fnoid; /* function to be called */
char enabled; /* as SESSION_REPLICATION_ROLE_* */
- int ntags; /* number of command tags */
- char **tag; /* command tags in SORTED order */
+ Bitmapset *tagset; /* command tags, or NULL if empty */
} EventTriggerCacheItem;
The code in evtcache.c is shorter and, in my opinion, easier to read. In filter_event_trigger, rather than running bsearch through a sorted array of strings, it just runs bms_is_member.
I’ve kept this change to the event trigger code in its own separate patch file, to make the change easier to review in isolation.
I’ll include stats about code-size and speed when I post v2.
The benchmarks are from tpc-b_96.sql. I think I’ll need to adjust the benchmark to put more emphasis on the particular code that I’m changing, but I have run this standard benchmark for this email:
For master (1fd687a035):
postgresql % find src -type f -name "*.c" -or -name "*.h" | xargs cat | wc
1482117 5690660 45256959
postgresql % find src -type f -name "*.o" | xargs cat | wc
38283 476264 18999164
Averages for test set 1 by scale:
set scale tps avg_latency 90%< max_latency
1 1 3741 1.734 3.162 133.718
1 9 6124 0.904 1.05 230.547
1 81 5921 0.931 1.015 67.023
Averages for test set 1 by clients:
set clients tps avg_latency 90%< max_latency
1 1 2163 0.461 0.514 24.414
1 4 5968 0.675 0.791 40.354
1 16 7655 2.433 3.922 366.519
For command tag patch (branched from 1fd687a035):
postgresql % find src -type f -name "*.c" -or -name "*.h" | xargs cat | wc
1482969 5691908 45281399
postgresql % find src -type f -name "*.o" | xargs cat | wc
38209 476243 18999752
Averages for test set 1 by scale:
set scale tps avg_latency 90%< max_latency
1 1 3877 1.645 3.066 24.973
1 9 6383 0.859 1.032 64.566
1 81 5945 0.925 1.023 162.9
Averages for test set 1 by clients:
set clients tps avg_latency 90%< max_latency
1 1 2141 0.466 0.522 11.531
1 4 5967 0.673 0.783 136.882
1 16 8096 2.292 3.817 104.026
Attachments:
v2-0001-Migrating-commandTag-from-string-to-enum.patchapplication/octet-stream; name=v2-0001-Migrating-commandTag-from-string-to-enum.patch; x-unix-mode=0644Download
From 981df43fbe9df2877500e8d177f0e74ef9ccd546 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Sun, 2 Feb 2020 10:14:26 -0800
Subject: [PATCH v2 1/3] Migrating commandTag from string to enum.
The backend was using strings to represent command tags and doing string
comparisons in multiple places. Fixing that by creating a new
CommandTag enum and using it instead.
Replacing numerous occurrences of char *completionTag with a
QueryCompletionData struct so that the code no longer stores information
about completed queries in a cstring. Only at the last moment, in
EndCommand(), does this get converted to a string.
---
.../pg_stat_statements/pg_stat_statements.c | 18 +-
contrib/sepgsql/hooks.c | 6 +-
src/backend/commands/async.c | 2 +-
src/backend/commands/copy.c | 2 +-
src/backend/commands/createas.c | 14 +-
src/backend/commands/event_trigger.c | 377 +++++++---
src/backend/commands/matview.c | 2 +-
src/backend/commands/portalcmds.c | 16 +-
src/backend/commands/prepare.c | 4 +-
src/backend/commands/sequence.c | 8 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/spi.c | 22 +-
src/backend/replication/walsender.c | 18 +-
src/backend/tcop/dest.c | 42 +-
src/backend/tcop/postgres.c | 24 +-
src/backend/tcop/pquery.c | 123 ++--
src/backend/tcop/utility.c | 643 +++++++++---------
src/backend/utils/adt/txid.c | 2 +-
src/backend/utils/cache/plancache.c | 4 +-
src/backend/utils/misc/Makefile | 1 +
src/backend/utils/misc/commandtag.c | 320 +++++++++
src/backend/utils/mmgr/portalmem.c | 7 +-
src/include/commands/createas.h | 2 +-
src/include/commands/matview.h | 2 +-
src/include/commands/portalcmds.h | 2 +-
src/include/commands/prepare.h | 2 +-
src/include/miscadmin.h | 10 +-
src/include/nodes/parsenodes.h | 1 +
src/include/tcop/dest.h | 6 +-
src/include/tcop/pquery.h | 2 +-
src/include/tcop/utility.h | 9 +-
src/include/utils/commandtag.h | 248 +++++++
src/include/utils/plancache.h | 6 +-
src/include/utils/portal.h | 6 +-
src/include/utils/querycompletion.h | 60 ++
src/pl/plpgsql/src/pl_exec.c | 7 +-
.../test_ddl_deparse/test_ddl_deparse.c | 2 +-
37 files changed, 1439 insertions(+), 585 deletions(-)
create mode 100644 src/backend/utils/misc/commandtag.c
create mode 100644 src/include/utils/commandtag.h
create mode 100644 src/include/utils/querycompletion.h
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 6f82a671ee..cd1d98114e 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -307,7 +307,7 @@ static void pgss_ExecutorEnd(QueryDesc *queryDesc);
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
static uint64 pgss_hash_string(const char *str, int len);
static void pgss_store(const char *query, uint64 queryId,
int query_location, int query_len,
@@ -960,7 +960,7 @@ static void
pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context,
ParamListInfo params, QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletionData *qc)
{
Node *parsetree = pstmt->utilityStmt;
@@ -998,11 +998,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
@@ -1013,10 +1013,8 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
INSTR_TIME_SET_CURRENT(duration);
INSTR_TIME_SUBTRACT(duration, start);
- /* parse command tag to retrieve the number of affected rows. */
- if (completionTag &&
- strncmp(completionTag, "COPY ", 5) == 0)
- rows = pg_strtouint64(completionTag + 5, NULL, 10);
+ if (qc && qc->commandTag == COMMANDTAG_COPY)
+ rows = qc->nprocessed;
else
rows = 0;
@@ -1060,11 +1058,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
}
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 997a64c87e..999a67bd00 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -317,7 +317,7 @@ sepgsql_utility_command(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
Node *parsetree = pstmt->utilityStmt;
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
@@ -380,11 +380,11 @@ sepgsql_utility_command(PlannedStmt *pstmt,
if (next_ProcessUtility_hook)
(*next_ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 9aa2b61600..5322c14ce4 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -594,7 +594,7 @@ pg_notify(PG_FUNCTION_ARGS)
payload = text_to_cstring(PG_GETARG_TEXT_PP(1));
/* For NOTIFY as a statement, this is checked in ProcessUtility */
- PreventCommandDuringRecovery("NOTIFY");
+ PreventCommandDuringRecovery(COMMANDTAG_NOTIFY);
Async_Notify(channel, payload);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 40a8ec1abd..4828e75bd5 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1063,7 +1063,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
/* check read-only transaction and parallel mode */
if (XactReadOnly && !rel->rd_islocaltemp)
- PreventCommandIfReadOnly("COPY FROM");
+ PreventCommandIfReadOnly(COMMANDTAG_COPY_FROM);
cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cc02cf824e..a969c777be 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -10,7 +10,7 @@
*
* Formerly, CTAS was implemented as a variant of SELECT, which led
* to assorted legacy behaviors that we still try to preserve, notably that
- * we must return a tuples-processed count in the completionTag. (We no
+ * we must return a tuples-processed count in the qcdata. (We no
* longer do that for CTAS ... WITH NO DATA, however.)
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
@@ -225,7 +225,7 @@ create_ctas_nodata(List *tlist, IntoClause *into)
ObjectAddress
ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ParamListInfo params, QueryEnvironment *queryEnv,
- char *completionTag)
+ QueryCompletionData *qc)
{
Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
@@ -270,7 +270,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
Assert(!is_matview); /* excluded by syntax */
- ExecuteQuery(pstate, estmt, into, params, dest, completionTag);
+ ExecuteQuery(pstate, estmt, into, params, dest, qc);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
@@ -352,11 +352,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* run the plan to completion */
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
- /* save the rowcount if we're given a completionTag to fill */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ /* save the rowcount if we're given a qc to fill */
+ if (qc)
+ SetQC(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed, DISPLAYFORMAT_NPROCESSED);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 71911d4067..6cd9437367 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -41,6 +41,7 @@
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/commandtag.h"
#include "utils/evtcache.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
@@ -85,52 +86,6 @@ typedef enum
EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED
} event_trigger_command_tag_check_result;
-/* XXX merge this with ObjectTypeMap? */
-static const event_trigger_support_data event_trigger_support[] = {
- {"ACCESS METHOD", true},
- {"AGGREGATE", true},
- {"CAST", true},
- {"CONSTRAINT", true},
- {"COLLATION", true},
- {"CONVERSION", true},
- {"DATABASE", false},
- {"DOMAIN", true},
- {"EXTENSION", true},
- {"EVENT TRIGGER", false},
- {"FOREIGN DATA WRAPPER", true},
- {"FOREIGN TABLE", true},
- {"FUNCTION", true},
- {"INDEX", true},
- {"LANGUAGE", true},
- {"MATERIALIZED VIEW", true},
- {"OPERATOR", true},
- {"OPERATOR CLASS", true},
- {"OPERATOR FAMILY", true},
- {"POLICY", true},
- {"PROCEDURE", true},
- {"PUBLICATION", true},
- {"ROLE", false},
- {"ROUTINE", true},
- {"RULE", true},
- {"SCHEMA", true},
- {"SEQUENCE", true},
- {"SERVER", true},
- {"STATISTICS", true},
- {"SUBSCRIPTION", true},
- {"TABLE", true},
- {"TABLESPACE", false},
- {"TRANSFORM", true},
- {"TRIGGER", true},
- {"TEXT SEARCH CONFIGURATION", true},
- {"TEXT SEARCH DICTIONARY", true},
- {"TEXT SEARCH PARSER", true},
- {"TEXT SEARCH TEMPLATE", true},
- {"TYPE", true},
- {"USER MAPPING", true},
- {"VIEW", true},
- {NULL, false}
-};
-
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -150,8 +105,8 @@ typedef struct SQLDropObject
static void AlterEventTriggerOwner_internal(Relation rel,
HeapTuple tup,
Oid newOwnerId);
-static event_trigger_command_tag_check_result check_ddl_tag(const char *tag);
-static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(const char *tag);
+static event_trigger_command_tag_check_result check_ddl_tag(CommandTag commandTag);
+static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(CommandTag commandTag);
static void error_duplicate_filter_variable(const char *defname);
static Datum filter_list_to_array(List *filterlist);
static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname,
@@ -259,69 +214,260 @@ validate_ddl_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
event_trigger_command_tag_check_result result;
- result = check_ddl_tag(tag);
+ result = check_ddl_tag(commandTag);
if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
- tag, filtervar)));
+ tagstr, filtervar)));
if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
static event_trigger_command_tag_check_result
-check_ddl_tag(const char *tag)
+check_ddl_tag(CommandTag commandTag)
{
- const char *obtypename;
- const event_trigger_support_data *etsd;
+ switch (commandTag)
+ {
+ /*
+ * Supported idiosyncratic special cases.
+ */
+ case COMMANDTAG_ALTER_DEFAULT_PRIVILEGES:
+ case COMMANDTAG_ALTER_LARGE_OBJECT:
+ case COMMANDTAG_COMMENT:
+ case COMMANDTAG_CREATE_TABLE_AS:
+ case COMMANDTAG_DROP_OWNED:
+ case COMMANDTAG_GRANT:
+ case COMMANDTAG_IMPORT_FOREIGN_SCHEMA:
+ case COMMANDTAG_REFRESH_MATERIALIZED_VIEW:
+ case COMMANDTAG_REVOKE:
+ case COMMANDTAG_SECURITY_LABEL:
+ case COMMANDTAG_SELECT_INTO:
- /*
- * Handle some idiosyncratic special cases.
- */
- if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
- pg_strcasecmp(tag, "SELECT INTO") == 0 ||
- pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
- pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
- pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
- pg_strcasecmp(tag, "COMMENT") == 0 ||
- pg_strcasecmp(tag, "GRANT") == 0 ||
- pg_strcasecmp(tag, "REVOKE") == 0 ||
- pg_strcasecmp(tag, "DROP OWNED") == 0 ||
- pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
+ /*
+ * Supported CREATE commands
+ */
+ case COMMANDTAG_CREATE_ACCESS_METHOD:
+ case COMMANDTAG_CREATE_AGGREGATE:
+ case COMMANDTAG_CREATE_CAST:
+ case COMMANDTAG_CREATE_COLLATION:
+ case COMMANDTAG_CREATE_CONSTRAINT:
+ case COMMANDTAG_CREATE_CONVERSION:
+ case COMMANDTAG_CREATE_DOMAIN:
+ case COMMANDTAG_CREATE_EXTENSION:
+ case COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER:
+ case COMMANDTAG_CREATE_FOREIGN_TABLE:
+ case COMMANDTAG_CREATE_FUNCTION:
+ case COMMANDTAG_CREATE_INDEX:
+ case COMMANDTAG_CREATE_LANGUAGE:
+ case COMMANDTAG_CREATE_MATERIALIZED_VIEW:
+ case COMMANDTAG_CREATE_OPERATOR:
+ case COMMANDTAG_CREATE_OPERATOR_CLASS:
+ case COMMANDTAG_CREATE_OPERATOR_FAMILY:
+ case COMMANDTAG_CREATE_POLICY:
+ case COMMANDTAG_CREATE_PROCEDURE:
+ case COMMANDTAG_CREATE_PUBLICATION:
+ case COMMANDTAG_CREATE_ROUTINE:
+ case COMMANDTAG_CREATE_RULE:
+ case COMMANDTAG_CREATE_SCHEMA:
+ case COMMANDTAG_CREATE_SEQUENCE:
+ case COMMANDTAG_CREATE_SERVER:
+ case COMMANDTAG_CREATE_STATISTICS:
+ case COMMANDTAG_CREATE_SUBSCRIPTION:
+ case COMMANDTAG_CREATE_TABLE:
+ case COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION:
+ case COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY:
+ case COMMANDTAG_CREATE_TEXT_SEARCH_PARSER:
+ case COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE:
+ case COMMANDTAG_CREATE_TRANSFORM:
+ case COMMANDTAG_CREATE_TRIGGER:
+ case COMMANDTAG_CREATE_TYPE:
+ case COMMANDTAG_CREATE_USER_MAPPING:
+ case COMMANDTAG_CREATE_VIEW:
- /*
- * Otherwise, command should be CREATE, ALTER, or DROP.
- */
- if (pg_strncasecmp(tag, "CREATE ", 7) == 0)
- obtypename = tag + 7;
- else if (pg_strncasecmp(tag, "ALTER ", 6) == 0)
- obtypename = tag + 6;
- else if (pg_strncasecmp(tag, "DROP ", 5) == 0)
- obtypename = tag + 5;
- else
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
+ /*
+ * Supported ALTER commands
+ */
+ case COMMANDTAG_ALTER_ACCESS_METHOD:
+ case COMMANDTAG_ALTER_AGGREGATE:
+ case COMMANDTAG_ALTER_CAST:
+ case COMMANDTAG_ALTER_COLLATION:
+ case COMMANDTAG_ALTER_CONSTRAINT:
+ case COMMANDTAG_ALTER_CONVERSION:
+ case COMMANDTAG_ALTER_DOMAIN:
+ case COMMANDTAG_ALTER_EXTENSION:
+ case COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER:
+ case COMMANDTAG_ALTER_FOREIGN_TABLE:
+ case COMMANDTAG_ALTER_FUNCTION:
+ case COMMANDTAG_ALTER_INDEX:
+ case COMMANDTAG_ALTER_LANGUAGE:
+ case COMMANDTAG_ALTER_MATERIALIZED_VIEW:
+ case COMMANDTAG_ALTER_OPERATOR:
+ case COMMANDTAG_ALTER_OPERATOR_CLASS:
+ case COMMANDTAG_ALTER_OPERATOR_FAMILY:
+ case COMMANDTAG_ALTER_POLICY:
+ case COMMANDTAG_ALTER_PROCEDURE:
+ case COMMANDTAG_ALTER_PUBLICATION:
+ case COMMANDTAG_ALTER_ROUTINE:
+ case COMMANDTAG_ALTER_RULE:
+ case COMMANDTAG_ALTER_SCHEMA:
+ case COMMANDTAG_ALTER_SEQUENCE:
+ case COMMANDTAG_ALTER_SERVER:
+ case COMMANDTAG_ALTER_STATISTICS:
+ case COMMANDTAG_ALTER_SUBSCRIPTION:
+ case COMMANDTAG_ALTER_TABLE:
+ case COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION:
+ case COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY:
+ case COMMANDTAG_ALTER_TEXT_SEARCH_PARSER:
+ case COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE:
+ case COMMANDTAG_ALTER_TRANSFORM:
+ case COMMANDTAG_ALTER_TRIGGER:
+ case COMMANDTAG_ALTER_TYPE:
+ case COMMANDTAG_ALTER_USER_MAPPING:
+ case COMMANDTAG_ALTER_VIEW:
- /*
- * ...and the object type should be something recognizable.
- */
- for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++)
- if (pg_strcasecmp(etsd->obtypename, obtypename) == 0)
+ /*
+ * Supported DROP commands
+ */
+ case COMMANDTAG_DROP_ACCESS_METHOD:
+ case COMMANDTAG_DROP_AGGREGATE:
+ case COMMANDTAG_DROP_CAST:
+ case COMMANDTAG_DROP_COLLATION:
+ case COMMANDTAG_DROP_CONSTRAINT:
+ case COMMANDTAG_DROP_CONVERSION:
+ case COMMANDTAG_DROP_DOMAIN:
+ case COMMANDTAG_DROP_EXTENSION:
+ case COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER:
+ case COMMANDTAG_DROP_FOREIGN_TABLE:
+ case COMMANDTAG_DROP_FUNCTION:
+ case COMMANDTAG_DROP_INDEX:
+ case COMMANDTAG_DROP_LANGUAGE:
+ case COMMANDTAG_DROP_MATERIALIZED_VIEW:
+ case COMMANDTAG_DROP_OPERATOR:
+ case COMMANDTAG_DROP_OPERATOR_CLASS:
+ case COMMANDTAG_DROP_OPERATOR_FAMILY:
+ case COMMANDTAG_DROP_POLICY:
+ case COMMANDTAG_DROP_PROCEDURE:
+ case COMMANDTAG_DROP_PUBLICATION:
+ case COMMANDTAG_DROP_ROUTINE:
+ case COMMANDTAG_DROP_RULE:
+ case COMMANDTAG_DROP_SCHEMA:
+ case COMMANDTAG_DROP_SEQUENCE:
+ case COMMANDTAG_DROP_SERVER:
+ case COMMANDTAG_DROP_STATISTICS:
+ case COMMANDTAG_DROP_SUBSCRIPTION:
+ case COMMANDTAG_DROP_TABLE:
+ case COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION:
+ case COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY:
+ case COMMANDTAG_DROP_TEXT_SEARCH_PARSER:
+ case COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE:
+ case COMMANDTAG_DROP_TRANSFORM:
+ case COMMANDTAG_DROP_TRIGGER:
+ case COMMANDTAG_DROP_TYPE:
+ case COMMANDTAG_DROP_USER_MAPPING:
+ case COMMANDTAG_DROP_VIEW:
+ return EVENT_TRIGGER_COMMAND_TAG_OK;
+
+ /*
+ * Unsupported CREATE commands
+ */
+ case COMMANDTAG_CREATE_DATABASE:
+ case COMMANDTAG_CREATE_EVENT_TRIGGER:
+ case COMMANDTAG_CREATE_ROLE:
+ case COMMANDTAG_CREATE_TABLESPACE:
+
+ /*
+ * Unsupported ALTER commands
+ */
+ case COMMANDTAG_ALTER_DATABASE:
+ case COMMANDTAG_ALTER_EVENT_TRIGGER:
+ case COMMANDTAG_ALTER_ROLE:
+ case COMMANDTAG_ALTER_TABLESPACE:
+
+ /*
+ * Unsupported DROP commands
+ */
+ case COMMANDTAG_DROP_DATABASE:
+ case COMMANDTAG_DROP_EVENT_TRIGGER:
+ case COMMANDTAG_DROP_ROLE:
+ case COMMANDTAG_DROP_TABLESPACE:
+
+ /*
+ * Other unsupported commands. These used to return
+ * EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED prior to the
+ * conversion of commandTag from string to enum.
+ */
+ case COMMANDTAG_ALTER_SYSTEM:
+ case COMMANDTAG_ANALYZE:
+ case COMMANDTAG_BEGIN:
+ case COMMANDTAG_CALL:
+ case COMMANDTAG_CHECKPOINT:
+ case COMMANDTAG_CLOSE:
+ case COMMANDTAG_CLOSE_CURSOR:
+ case COMMANDTAG_CLOSE_CURSOR_ALL:
+ case COMMANDTAG_CLUSTER:
+ case COMMANDTAG_COMMIT:
+ case COMMANDTAG_COMMIT_PREPARED:
+ case COMMANDTAG_COPY:
+ case COMMANDTAG_COPY_FROM:
+ case COMMANDTAG_DEALLOCATE:
+ case COMMANDTAG_DEALLOCATE_ALL:
+ case COMMANDTAG_DECLARE_CURSOR:
+ case COMMANDTAG_DELETE:
+ case COMMANDTAG_DISCARD:
+ case COMMANDTAG_DISCARD_ALL:
+ case COMMANDTAG_DISCARD_PLANS:
+ case COMMANDTAG_DISCARD_SEQUENCES:
+ case COMMANDTAG_DISCARD_TEMP:
+ case COMMANDTAG_DO:
+ case COMMANDTAG_DROP_REPLICATION_SLOT:
+ case COMMANDTAG_EXECUTE:
+ case COMMANDTAG_EXPLAIN:
+ case COMMANDTAG_FETCH:
+ case COMMANDTAG_GRANT_ROLE:
+ case COMMANDTAG_INSERT:
+ case COMMANDTAG_LISTEN:
+ case COMMANDTAG_LOAD:
+ case COMMANDTAG_LOCK_TABLE:
+ case COMMANDTAG_MOVE:
+ case COMMANDTAG_NOTIFY:
+ case COMMANDTAG_PREPARE:
+ case COMMANDTAG_PREPARE_TRANSACTION:
+ case COMMANDTAG_REASSIGN_OWNED:
+ case COMMANDTAG_REINDEX:
+ case COMMANDTAG_RELEASE:
+ case COMMANDTAG_RESET:
+ case COMMANDTAG_REVOKE_ROLE:
+ case COMMANDTAG_ROLLBACK:
+ case COMMANDTAG_ROLLBACK_PREPARED:
+ case COMMANDTAG_SAVEPOINT:
+ case COMMANDTAG_SELECT:
+ case COMMANDTAG_SELECT_FOR_KEY_SHARE:
+ case COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE:
+ case COMMANDTAG_SELECT_FOR_SHARE:
+ case COMMANDTAG_SELECT_FOR_UPDATE:
+ case COMMANDTAG_SET:
+ case COMMANDTAG_SET_CONSTRAINTS:
+ case COMMANDTAG_SHOW:
+ case COMMANDTAG_START_TRANSACTION:
+ case COMMANDTAG_TRUNCATE_TABLE:
+ case COMMANDTAG_UNLISTEN:
+ case COMMANDTAG_UPDATE:
+ case COMMANDTAG_VACUUM:
+ return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
+ case COMMANDTAG_UNKNOWN:
break;
- if (etsd->obtypename == NULL)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
- if (!etsd->supported)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
- return EVENT_TRIGGER_COMMAND_TAG_OK;
+ }
+ return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
}
/*
@@ -334,26 +480,37 @@ validate_table_rewrite_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
event_trigger_command_tag_check_result result;
- result = check_table_rewrite_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s represents an SQL statement name */
- errmsg("event triggers are not supported for %s",
- tag)));
+ result = check_table_rewrite_ddl_tag(commandTag);
+ switch (result)
+ {
+ case EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED:
+ case EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ /* translator: %s represents an SQL statement name */
+ errmsg("event triggers are not supported for %s",
+ tagstr)));
+ case EVENT_TRIGGER_COMMAND_TAG_OK:
+ break;
+ }
}
}
static event_trigger_command_tag_check_result
-check_table_rewrite_ddl_tag(const char *tag)
+check_table_rewrite_ddl_tag(CommandTag commandTag)
{
- if (pg_strcasecmp(tag, "ALTER TABLE") == 0 ||
- pg_strcasecmp(tag, "ALTER TYPE") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
+ switch (commandTag)
+ {
+ case COMMANDTAG_ALTER_TABLE:
+ case COMMANDTAG_ALTER_TYPE:
+ return EVENT_TRIGGER_COMMAND_TAG_OK;
+ default:
+ break;
+ }
return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
}
@@ -721,7 +878,7 @@ EventTriggerCommonSetup(Node *parsetree,
*/
#ifdef USE_ASSERT_CHECKING
{
- const char *dbgtag;
+ CommandTag dbgtag;
dbgtag = CreateCommandTag(parsetree);
if (event == EVT_DDLCommandStart ||
@@ -729,12 +886,12 @@ EventTriggerCommonSetup(Node *parsetree,
event == EVT_SQLDrop)
{
if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
else if (event == EVT_TableRewrite)
{
if (check_table_rewrite_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
}
#endif
@@ -745,7 +902,7 @@ EventTriggerCommonSetup(Node *parsetree,
return NIL;
/* Get the command tag. */
- tag = CreateCommandTag(parsetree);
+ tag = GetCommandTagName(CreateCommandTag(parsetree));
/*
* Filter list of event triggers by command tag, and copy them into our
@@ -2136,7 +2293,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
values[i++] = Int32GetDatum(addr.objectSubId);
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(GetCommandTagName(CreateCommandTag(cmd->parsetree)));
/* object_type */
values[i++] = CStringGetTextDatum(type);
/* schema */
@@ -2161,7 +2318,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
nulls[i++] = true;
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(GetCommandTagName(CreateCommandTag(cmd->parsetree)));
/* object_type */
values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
/* schema */
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 1ee37c1aeb..45eafd4472 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -136,7 +136,7 @@ SetMatViewPopulatedState(Relation relation, bool newstate)
*/
ObjectAddress
ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag)
+ ParamListInfo params, QueryCompletionData *qc)
{
Oid matviewOid;
Relation matviewRel;
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 7e5c805a1e..f4ac53a711 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -106,7 +106,8 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
PortalDefineQuery(portal,
NULL,
queryString,
- "SELECT", /* cursor's query is always a SELECT */
+ COMMANDTAG_SELECT, /* cursor's query is always a
+ * SELECT */
list_make1(plan),
NULL);
@@ -160,15 +161,14 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
*
* stmt: parsetree node for command
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store a command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*/
void
PerformPortalFetch(FetchStmt *stmt,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
Portal portal;
uint64 nprocessed;
@@ -203,10 +203,8 @@ PerformPortalFetch(FetchStmt *stmt,
dest);
/* Return command status if wanted */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT,
- stmt->ismove ? "MOVE" : "FETCH",
- nprocessed);
+ if (qc)
+ SetQC(qc, stmt->ismove ? COMMANDTAG_MOVE : COMMANDTAG_FETCH, nprocessed, DISPLAYFORMAT_NPROCESSED);
}
/*
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index c4e4b6eaec..18f6227fde 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -187,7 +187,7 @@ void
ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletionData *qc)
{
PreparedStatement *entry;
CachedPlan *cplan;
@@ -288,7 +288,7 @@ ExecuteQuery(ParseState *pstate,
*/
PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
- (void) PortalRun(portal, count, false, true, dest, dest, completionTag);
+ (void) PortalRun(portal, count, false, true, dest, dest, qc);
PortalDrop(portal, false);
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 6aab73bfd4..d6c06fec9f 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -612,14 +612,14 @@ nextval_internal(Oid relid, bool check_permissions)
/* read-only transactions may only modify temp sequences */
if (!seqrel->rd_islocaltemp)
- PreventCommandIfReadOnly("nextval()");
+ PreventCommandStrIfReadOnly("nextval()");
/*
* Forbid this during parallel operation because, to make it work, the
* cooperating backends would need to share the backend-local cached
* sequence information. Currently, we don't support that.
*/
- PreventCommandIfParallelMode("nextval()");
+ PreventCommandStrIfParallelMode("nextval()");
if (elm->last != elm->cached) /* some numbers were cached */
{
@@ -937,14 +937,14 @@ do_setval(Oid relid, int64 next, bool iscalled)
/* read-only transactions may only modify temp sequences */
if (!seqrel->rd_islocaltemp)
- PreventCommandIfReadOnly("setval()");
+ PreventCommandStrIfReadOnly("setval()");
/*
* Forbid this during parallel operation because, to make it work, the
* cooperating backends would need to share the backend-local cached
* sequence information. Currently, we don't support that.
*/
- PreventCommandIfParallelMode("setval()");
+ PreventCommandStrIfParallelMode("setval()");
/* lock page' buffer and read tuple */
seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 5cff6c4321..57ae9b6c7f 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -530,7 +530,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a SQL function",
- CreateCommandTag(stmt->utilityStmt))));
+ GetCommandTagName(CreateCommandTag(stmt->utilityStmt)))));
}
if (fcache->readonly_func && !CommandIsReadOnly(stmt))
@@ -538,7 +538,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ GetCommandTagName(CreateCommandTag((Node *) stmt)))));
/* OK, build the execution_state for this query */
newes = (execution_state *) palloc(sizeof(execution_state));
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index c46764bf42..04365beac9 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1338,7 +1338,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
/* translator: %s is name of a SQL command, eg INSERT */
errmsg("cannot open %s query as cursor",
- plansource->commandTag)));
+ GetCommandTagName(plansource->commandTag))));
}
Assert(list_length(plan->plancache_list) == 1);
@@ -1469,7 +1469,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) pstmt))));
+ GetCommandTagName(CreateCommandTag((Node *) pstmt)))));
}
}
@@ -2255,7 +2255,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ GetCommandTagName(CreateCommandTag((Node *) stmt)))));
/*
* If not read-only mode, advance the command counter before each
@@ -2291,9 +2291,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else
{
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletionData qcdata;
ProcessUtilityContext context;
+ InitializeQC(&qcdata);
+
/*
* If the SPI context is atomic, or we are asked to manage
* snapshots, then we are in an atomic execution context.
@@ -2312,7 +2314,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
paramLI,
_SPI_current->queryEnv,
dest,
- completionTag);
+ &qcdata);
/* Update "processed" if stmt returned tuples */
if (_SPI_current->tuptable)
@@ -2328,9 +2330,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
{
CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
- if (strncmp(completionTag, "SELECT ", 7) == 0)
- _SPI_current->processed =
- pg_strtouint64(completionTag + 7, NULL, 10);
+ if (qcdata.commandTag == COMMANDTAG_SELECT)
+ _SPI_current->processed = qcdata.nprocessed;
else
{
/*
@@ -2351,9 +2352,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else if (IsA(stmt->utilityStmt, CopyStmt))
{
- Assert(strncmp(completionTag, "COPY ", 5) == 0);
- _SPI_current->processed = pg_strtouint64(completionTag + 5,
- NULL, 10);
+ Assert(qcdata.commandTag == COMMANDTAG_COPY);
+ _SPI_current->processed = qcdata.nprocessed;
}
}
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index abb533b9d0..78169868c4 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1074,8 +1074,11 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
static void
DropReplicationSlot(DropReplicationSlotCmd *cmd)
{
+ QueryCompletionData qcdata;
+
ReplicationSlotDrop(cmd->slotname, !cmd->wait);
- EndCommand("DROP_REPLICATION_SLOT", DestRemote);
+ SetQC(&qcdata, COMMANDTAG_DROP_REPLICATION_SLOT, 0, DISPLAYFORMAT_PLAIN);
+ EndCommand(&qcdata, DestRemote);
}
/*
@@ -1086,6 +1089,7 @@ static void
StartLogicalReplication(StartReplicationCmd *cmd)
{
StringInfoData buf;
+ QueryCompletionData qcdata;
/* make sure that our requirements are still fulfilled */
CheckLogicalDecodingRequirements();
@@ -1160,7 +1164,8 @@ StartLogicalReplication(StartReplicationCmd *cmd)
WalSndSetState(WALSNDSTATE_STARTUP);
/* Get out of COPY mode (CommandComplete). */
- EndCommand("COPY 0", DestRemote);
+ SetQC(&qcdata, COMMANDTAG_COPY, 0, DISPLAYFORMAT_NPROCESSED);
+ EndCommand(&qcdata, DestRemote);
}
/*
@@ -1464,6 +1469,7 @@ exec_replication_command(const char *cmd_string)
Node *cmd_node;
MemoryContext cmd_context;
MemoryContext old_context;
+ QueryCompletionData qcdata;
/*
* If WAL sender has been told that shutdown is getting close, switch its
@@ -1614,7 +1620,8 @@ exec_replication_command(const char *cmd_string)
MemoryContextDelete(cmd_context);
/* Send CommandComplete message */
- EndCommand("SELECT", DestRemote);
+ SetQC(&qcdata, COMMANDTAG_SELECT, 0, DISPLAYFORMAT_PLAIN);
+ EndCommand(&qcdata, DestRemote);
/* Report to pgstat that this process is now idle */
pgstat_report_activity(STATE_IDLE, NULL);
@@ -2867,8 +2874,11 @@ WalSndDone(WalSndSendDataCallback send_data)
if (WalSndCaughtUp && sentPtr == replicatedPtr &&
!pq_is_send_pending())
{
+ QueryCompletionData qcdata;
+
/* Inform the standby that XLOG streaming is done */
- EndCommand("COPY 0", DestRemote);
+ SetQC(&qcdata, COMMANDTAG_COPY, 0, DISPLAYFORMAT_NPROCESSED);
+ EndCommand(&qcdata, DestRemote);
pq_flush();
proc_exit(0);
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 09c1dcbb53..ba1416da00 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -100,7 +100,7 @@ DestReceiver *None_Receiver = (DestReceiver *) &donothingDR;
* ----------------
*/
void
-BeginCommand(const char *commandTag, CommandDest dest)
+BeginCommand(CommandTag commandTag, CommandDest dest)
{
/* Nothing to do at present */
}
@@ -163,8 +163,11 @@ CreateDestReceiver(CommandDest dest)
* ----------------
*/
void
-EndCommand(const char *commandTag, CommandDest dest)
+EndCommand(const QueryCompletionData *qc, CommandDest dest)
{
+ char completionTag[COMPLETION_TAG_BUFSIZE];
+ const char *tagname;
+
switch (dest)
{
case DestRemote:
@@ -172,11 +175,38 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestRemoteSimple:
/*
- * We assume the commandTag is plain ASCII and therefore requires
- * no encoding conversion.
+ * We assume the tagname is plain ASCII and therefore
+ * requires no encoding conversion.
*/
- pq_putmessage('C', commandTag, strlen(commandTag) + 1);
- break;
+ tagname = GetCommandTagName(qc->commandTag);
+ switch (qc->display_format)
+ {
+ case DISPLAYFORMAT_PLAIN:
+ pq_putmessage('C', tagname, strlen(tagname) + 1);
+ break;
+ case DISPLAYFORMAT_LAST_OID:
+ /*
+ * We no longer display LastOid, but to preserve the wire protocol,
+ * we write InvalidOid where the LastOid used to be written. For
+ * efficiency in the snprintf(), hard-code InvalidOid as zero.
+ */
+ Assert(InvalidOid == 0);
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "%s 0 " UINT64_FORMAT,
+ tagname,
+ qc->nprocessed);
+ pq_putmessage('C', completionTag, strlen(completionTag) + 1);
+ break;
+ case DISPLAYFORMAT_NPROCESSED:
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "%s " UINT64_FORMAT,
+ tagname,
+ qc->nprocessed);
+ pq_putmessage('C', completionTag, strlen(completionTag) + 1);
+ break;
+ default:
+ elog(ERROR, "Invalid display_format in EndCommand");
+ }
case DestNone:
case DestDebug:
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0a6f80963b..a3ecd27547 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1064,8 +1064,8 @@ exec_simple_query(const char *query_string)
{
RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);
bool snapshot_set = false;
- const char *commandTag;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag commandTag;
+ QueryCompletionData qcdata;
MemoryContext per_parsetree_context = NULL;
List *querytree_list,
*plantree_list;
@@ -1081,7 +1081,7 @@ exec_simple_query(const char *query_string)
*/
commandTag = CreateCommandTag(parsetree->stmt);
- set_ps_display(commandTag, false);
+ set_ps_display(GetCommandTagName(commandTag), false);
BeginCommand(commandTag, dest);
@@ -1230,7 +1230,7 @@ exec_simple_query(const char *query_string)
true,
receiver,
receiver,
- completionTag);
+ &qcdata);
receiver->rDestroy(receiver);
@@ -1281,7 +1281,7 @@ exec_simple_query(const char *query_string)
* command the client sent, regardless of rewriting. (But a command
* aborted by error will not send an EndCommand report at all.)
*/
- EndCommand(completionTag, dest);
+ EndCommand(&qcdata, dest);
/* Now we may drop the per-parsetree context, if one was created. */
if (per_parsetree_context)
@@ -1343,7 +1343,7 @@ exec_parse_message(const char *query_string, /* string to execute */
MemoryContext oldcontext;
List *parsetree_list;
RawStmt *raw_parse_tree;
- const char *commandTag;
+ CommandTag commandTag;
List *querytree_list;
CachedPlanSource *psrc;
bool is_named;
@@ -1505,7 +1505,7 @@ exec_parse_message(const char *query_string, /* string to execute */
{
/* Empty input string. This is legal. */
raw_parse_tree = NULL;
- commandTag = NULL;
+ commandTag = COMMANDTAG_UNKNOWN;
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
querytree_list = NIL;
}
@@ -2022,7 +2022,7 @@ exec_execute_message(const char *portal_name, long max_rows)
DestReceiver *receiver;
Portal portal;
bool completed;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletionData qcdata;
const char *sourceText;
const char *prepStmtName;
ParamListInfo portalParams;
@@ -2049,7 +2049,7 @@ exec_execute_message(const char *portal_name, long max_rows)
* If the original query was a null string, just return
* EmptyQueryResponse.
*/
- if (portal->commandTag == NULL)
+ if (portal->commandTag == COMMANDTAG_UNKNOWN)
{
Assert(portal->stmts == NIL);
NullCommand(dest);
@@ -2095,7 +2095,7 @@ exec_execute_message(const char *portal_name, long max_rows)
pgstat_report_activity(STATE_RUNNING, sourceText);
- set_ps_display(portal->commandTag, false);
+ set_ps_display(GetCommandTagName(portal->commandTag), false);
if (save_log_statement_stats)
ResetUsage();
@@ -2176,7 +2176,7 @@ exec_execute_message(const char *portal_name, long max_rows)
!execute_is_fetch && max_rows == FETCH_ALL,
receiver,
receiver,
- completionTag);
+ &qcdata);
receiver->rDestroy(receiver);
@@ -2209,7 +2209,7 @@ exec_execute_message(const char *portal_name, long max_rows)
}
/* Send appropriate CommandComplete to client */
- EndCommand(completionTag, dest);
+ EndCommand(&qcdata, dest);
}
else
{
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 0f5801e046..fe1a7672cb 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -40,7 +40,7 @@ static void ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletionData *qc);
static void FillPortalStore(Portal portal, bool isTopLevel);
static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
DestReceiver *dest);
@@ -48,11 +48,11 @@ static uint64 PortalRunSelect(Portal portal, bool forward, long count,
DestReceiver *dest);
static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
static void PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletionData *qc);
static uint64 DoPortalRunFetch(Portal portal,
FetchDirection fdirection,
long count,
@@ -125,10 +125,9 @@ FreeQueryDesc(QueryDesc *qdesc)
* sourceText: the source text of the query
* params: any parameters needed
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store the command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want a status string.
*
* Must be called in a memory context that will be reset or deleted on
* error; otherwise the executor's memory usage will be leaked.
@@ -139,7 +138,7 @@ ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
QueryDesc *queryDesc;
@@ -161,38 +160,26 @@ ProcessQuery(PlannedStmt *plan,
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
/*
- * Build command completion status string, if caller wants one.
+ * Build command completion status data, if caller wants one.
*/
- if (completionTag)
+ if (qc)
{
- Oid lastOid;
-
switch (queryDesc->operation)
{
case CMD_SELECT:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQC(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed, DISPLAYFORMAT_NPROCESSED);
break;
case CMD_INSERT:
- /* lastoid doesn't exist anymore */
- lastOid = InvalidOid;
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "INSERT %u " UINT64_FORMAT,
- lastOid, queryDesc->estate->es_processed);
+ SetQC(qc, COMMANDTAG_INSERT, queryDesc->estate->es_processed, DISPLAYFORMAT_LAST_OID);
break;
case CMD_UPDATE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "UPDATE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQC(qc, COMMANDTAG_UPDATE, queryDesc->estate->es_processed, DISPLAYFORMAT_NPROCESSED);
break;
case CMD_DELETE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "DELETE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQC(qc, COMMANDTAG_DELETE, queryDesc->estate->es_processed, DISPLAYFORMAT_NPROCESSED);
break;
default:
- strcpy(completionTag, "???");
+ SetQC(qc, COMMANDTAG_UNKNOWN, queryDesc->estate->es_processed, DISPLAYFORMAT_PLAIN);
break;
}
}
@@ -675,9 +662,8 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
*
* altdest: where to send output of non-primary queries
*
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
- * May be NULL if caller doesn't want a status string.
+ * qc: where to store command completion status data.
+ * May be NULL if caller doesn't want status data.
*
* Returns true if the portal's execution is complete, false if it was
* suspended due to exhaustion of the count parameter.
@@ -685,7 +671,7 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
bool
PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletionData *qc)
{
bool result;
uint64 nprocessed;
@@ -700,9 +686,9 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
TRACE_POSTGRESQL_QUERY_EXECUTE_START();
- /* Initialize completion tag to empty string */
- if (completionTag)
- completionTag[0] = '\0';
+ /* Initialize empty completion data */
+ if (qc)
+ InitializeQC(qc);
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
{
@@ -774,13 +760,12 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
* gave us a pointer to store it, copy it. Patch the "SELECT"
* tag to also provide the rowcount.
*/
- if (completionTag && portal->commandTag)
+ if (qc && portal->qcdata.commandTag != COMMANDTAG_UNKNOWN)
{
- if (strcmp(portal->commandTag, "SELECT") == 0)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT, nprocessed);
- else
- strcpy(completionTag, portal->commandTag);
+ CopyQC(qc, &portal->qcdata);
+ qc->nprocessed = nprocessed;
+ if (portal->commandTag == COMMANDTAG_SELECT)
+ qc->display_format = DISPLAYFORMAT_NPROCESSED;
}
/* Mark portal not active */
@@ -794,7 +779,7 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
case PORTAL_MULTI_QUERY:
PortalRunMulti(portal, isTopLevel, false,
- dest, altdest, completionTag);
+ dest, altdest, qc);
/* Prevent portal's commands from being re-executed */
MarkPortalDone(portal);
@@ -1005,8 +990,9 @@ static void
FillPortalStore(Portal portal, bool isTopLevel)
{
DestReceiver *treceiver;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletionData qcdata;
+ InitializeQC(&qcdata);
PortalCreateHoldStore(portal);
treceiver = CreateDestReceiver(DestTuplestore);
SetTuplestoreDestReceiverParams(treceiver,
@@ -1014,8 +1000,6 @@ FillPortalStore(Portal portal, bool isTopLevel)
portal->holdContext,
false);
- completionTag[0] = '\0';
-
switch (portal->strategy)
{
case PORTAL_ONE_RETURNING:
@@ -1028,12 +1012,12 @@ FillPortalStore(Portal portal, bool isTopLevel)
* portal's holdSnapshot to the snapshot used (or a copy of it).
*/
PortalRunMulti(portal, isTopLevel, true,
- treceiver, None_Receiver, completionTag);
+ treceiver, None_Receiver, &qcdata);
break;
case PORTAL_UTIL_SELECT:
PortalRunUtility(portal, linitial_node(PlannedStmt, portal->stmts),
- isTopLevel, true, treceiver, completionTag);
+ isTopLevel, true, treceiver, &qcdata);
break;
default:
@@ -1042,9 +1026,9 @@ FillPortalStore(Portal portal, bool isTopLevel)
break;
}
- /* Override default completion tag with actual command result */
- if (completionTag[0] != '\0')
- portal->commandTag = pstrdup(completionTag);
+ /* Override portal completion data with actual command results */
+ if (qcdata.commandTag != COMMANDTAG_UNKNOWN)
+ CopyQC(&portal->qcdata, &qcdata);
treceiver->rDestroy(treceiver);
}
@@ -1130,7 +1114,7 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count,
static void
PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletionData *qc)
{
Node *utilityStmt = pstmt->utilityStmt;
Snapshot snapshot;
@@ -1178,7 +1162,7 @@ PortalRunUtility(Portal portal, PlannedStmt *pstmt,
portal->portalParams,
portal->queryEnv,
dest,
- completionTag);
+ qc);
/* Some utility statements may change context on us */
MemoryContextSwitchTo(portal->portalContext);
@@ -1202,7 +1186,7 @@ static void
PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletionData *qc)
{
bool active_snapshot_set = false;
ListCell *stmtlist_item;
@@ -1284,7 +1268,7 @@ PortalRunMulti(Portal portal,
portal->sourceText,
portal->portalParams,
portal->queryEnv,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1319,7 +1303,7 @@ PortalRunMulti(Portal portal,
Assert(!active_snapshot_set);
/* statement can set tag string */
PortalRunUtility(portal, pstmt, isTopLevel, false,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1350,8 +1334,8 @@ PortalRunMulti(Portal portal,
PopActiveSnapshot();
/*
- * If a command completion tag was supplied, use it. Otherwise use the
- * portal's commandTag as the default completion tag.
+ * If a query completion data was supplied, use it. Otherwise use the
+ * portal's query completion data.
*
* Exception: Clients expect INSERT/UPDATE/DELETE tags to have counts, so
* fake them with zeros. This can happen with DO INSTEAD rules if there
@@ -1361,18 +1345,25 @@ PortalRunMulti(Portal portal,
* e.g. an INSERT that does an UPDATE instead should not print "0 1" if
* one row was updated. See QueryRewrite(), step 3, for details.
*/
- if (completionTag && completionTag[0] == '\0')
+ if (qc && qc->commandTag == COMMANDTAG_UNKNOWN)
{
- if (portal->commandTag)
- strcpy(completionTag, portal->commandTag);
- if (strcmp(completionTag, "SELECT") == 0)
- sprintf(completionTag, "SELECT 0 0");
- else if (strcmp(completionTag, "INSERT") == 0)
- strcpy(completionTag, "INSERT 0 0");
- else if (strcmp(completionTag, "UPDATE") == 0)
- strcpy(completionTag, "UPDATE 0");
- else if (strcmp(completionTag, "DELETE") == 0)
- strcpy(completionTag, "DELETE 0");
+ if (portal->qcdata.commandTag != COMMANDTAG_UNKNOWN)
+ CopyQC(qc, &portal->qcdata);
+ /* If the caller supplied a qc, we should have set it by now. */
+ Assert(qc->commandTag != COMMANDTAG_UNKNOWN);
+ switch (qc->commandTag)
+ {
+ case COMMANDTAG_SELECT:
+ case COMMANDTAG_INSERT:
+ qc->display_format = DISPLAYFORMAT_LAST_OID;
+ break;
+ case COMMANDTAG_UPDATE:
+ case COMMANDTAG_DELETE:
+ qc->display_format = DISPLAYFORMAT_NPROCESSED;
+ break;
+ default:
+ break;
+ }
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index bb85b5e52a..fb9ea6673a 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -83,7 +83,7 @@ static void ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletionData *qc);
static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
@@ -396,20 +396,33 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree)
}
/*
- * PreventCommandIfReadOnly: throw error if XactReadOnly
+ * PreventCommandStrIfReadOnly: throw error if XactReadOnly
*
* This is useful partly to ensure consistency of the error message wording;
* some callers have checked XactReadOnly for themselves.
*/
+static inline void
+PreventCommandReadOnly(const char *commandstr)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
+ /* translator: %s is name of a SQL command, eg CREATE */
+ errmsg("cannot execute %s in a read-only transaction",
+ commandstr)));
+}
+
void
-PreventCommandIfReadOnly(const char *cmdname)
+PreventCommandStrIfReadOnly(const char *commandstr)
{
if (XactReadOnly)
- ereport(ERROR,
- (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
- /* translator: %s is name of a SQL command, eg CREATE */
- errmsg("cannot execute %s in a read-only transaction",
- cmdname)));
+ PreventCommandReadOnly(commandstr);
+}
+
+void
+PreventCommandIfReadOnly(CommandTag commandTag)
+{
+ if (XactReadOnly)
+ PreventCommandReadOnly(GetCommandTagName(commandTag));
}
/*
@@ -419,15 +432,28 @@ PreventCommandIfReadOnly(const char *cmdname)
* This is useful partly to ensure consistency of the error message wording;
* some callers have checked IsInParallelMode() for themselves.
*/
+static inline void
+PreventCommandParallelMode(const char *commandstr)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
+ /* translator: %s is name of a SQL command, eg CREATE */
+ errmsg("cannot execute %s during a parallel operation",
+ commandstr)));
+}
+
void
-PreventCommandIfParallelMode(const char *cmdname)
+PreventCommandStrIfParallelMode(const char *commandstr)
{
if (IsInParallelMode())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- /* translator: %s is name of a SQL command, eg CREATE */
- errmsg("cannot execute %s during a parallel operation",
- cmdname)));
+ PreventCommandParallelMode(commandstr);
+}
+
+void
+PreventCommandIfParallelMode(CommandTag commandTag)
+{
+ if (IsInParallelMode())
+ PreventCommandParallelMode(GetCommandTagName(commandTag));
}
/*
@@ -438,15 +464,28 @@ PreventCommandIfParallelMode(const char *cmdname)
* commands that are allowed in "read-only" xacts but cannot be allowed
* in Hot Standby mode. Those commands should call this function.
*/
+static inline void
+PreventCommandRecovery(const char *commandstr)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
+ /* translator: %s is name of a SQL command, eg CREATE */
+ errmsg("cannot execute %s during recovery",
+ commandstr)));
+}
+
void
-PreventCommandDuringRecovery(const char *cmdname)
+PreventCommandStrDuringRecovery(const char *commandstr)
{
if (RecoveryInProgress())
- ereport(ERROR,
- (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
- /* translator: %s is name of a SQL command, eg CREATE */
- errmsg("cannot execute %s during recovery",
- cmdname)));
+ PreventCommandRecovery(commandstr);
+}
+
+void
+PreventCommandDuringRecovery(CommandTag commandTag)
+{
+ if (RecoveryInProgress())
+ PreventCommandRecovery(GetCommandTagName(commandTag));
}
/*
@@ -457,14 +496,14 @@ PreventCommandDuringRecovery(const char *cmdname)
* better-defined protection mechanism, such as ownership.
*/
static void
-CheckRestrictedOperation(const char *cmdname)
+CheckRestrictedOperation(CommandTag commandTag)
{
if (InSecurityRestrictedOperation())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
/* translator: %s is name of a SQL command, eg PREPARE */
errmsg("cannot execute %s within security-restricted operation",
- cmdname)));
+ GetCommandTagName(commandTag))));
}
@@ -480,16 +519,13 @@ CheckRestrictedOperation(const char *cmdname)
* queryEnv: environment for parse through execution (e.g., ephemeral named
* tables like trigger transition tables). May be NULL.
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store command completion status data.
*
* Caller MUST supply a queryString; it is not allowed (anymore) to pass NULL.
* If you really don't have source text, you can pass a constant string,
* perhaps "(query not available)".
*
- * completionTag is only set nonempty if we want to return a nondefault status.
- *
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*
* Note for users of ProcessUtility_hook: the same queryString may be passed
* to multiple invocations of ProcessUtility when processing a query string
@@ -507,7 +543,7 @@ ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
Assert(IsA(pstmt, PlannedStmt));
Assert(pstmt->commandType == CMD_UTILITY);
@@ -521,11 +557,11 @@ ProcessUtility(PlannedStmt *pstmt,
if (ProcessUtility_hook)
(*ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
/*
@@ -546,7 +582,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -562,7 +598,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (readonly_flags != COMMAND_IS_STRICTLY_READ_ONLY &&
(XactReadOnly || IsInParallelMode()))
{
- const char *commandtag = CreateCommandTag(parsetree);
+ CommandTag commandtag = CreateCommandTag(parsetree);
if ((readonly_flags & COMMAND_OK_IN_READ_ONLY_TXN) == 0)
PreventCommandIfReadOnly(commandtag);
@@ -572,8 +608,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
PreventCommandDuringRecovery(commandtag);
}
- if (completionTag)
- completionTag[0] = '\0';
+ if (qc)
+ InitializeQC(qc);
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -623,18 +659,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case TRANS_STMT_COMMIT:
if (!EndTransactionBlock(stmt->chain))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQC(qc, COMMANDTAG_ROLLBACK, 0, DISPLAYFORMAT_PLAIN);
}
break;
case TRANS_STMT_PREPARE:
if (!PrepareTransactionBlock(stmt->gid))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQC(qc, COMMANDTAG_ROLLBACK, 0, DISPLAYFORMAT_PLAIN);
}
break;
@@ -687,14 +723,13 @@ standard_ProcessUtility(PlannedStmt *pstmt,
{
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
- CheckRestrictedOperation("CLOSE");
+ CheckRestrictedOperation(COMMANDTAG_CLOSE);
PerformPortalClose(stmt->portalname);
}
break;
case T_FetchStmt:
- PerformPortalFetch((FetchStmt *) parsetree, dest,
- completionTag);
+ PerformPortalFetch((FetchStmt *) parsetree, dest, qc);
break;
case T_DoStmt:
@@ -729,14 +764,13 @@ standard_ProcessUtility(PlannedStmt *pstmt,
DoCopy(pstate, (CopyStmt *) parsetree,
pstmt->stmt_location, pstmt->stmt_len,
&processed);
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "COPY " UINT64_FORMAT, processed);
+ if (qc)
+ SetQC(qc, COMMANDTAG_COPY, processed, DISPLAYFORMAT_NPROCESSED);
}
break;
case T_PrepareStmt:
- CheckRestrictedOperation("PREPARE");
+ CheckRestrictedOperation(COMMANDTAG_PREPARE);
PrepareQuery(pstate, (PrepareStmt *) parsetree,
pstmt->stmt_location, pstmt->stmt_len);
break;
@@ -745,11 +779,11 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ExecuteQuery(pstate,
(ExecuteStmt *) parsetree, NULL,
params,
- dest, completionTag);
+ dest, qc);
break;
case T_DeallocateStmt:
- CheckRestrictedOperation("DEALLOCATE");
+ CheckRestrictedOperation(COMMANDTAG_DEALLOCATE);
DeallocateQuery((DeallocateStmt *) parsetree);
break;
@@ -793,7 +827,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
{
ListenStmt *stmt = (ListenStmt *) parsetree;
- CheckRestrictedOperation("LISTEN");
+ CheckRestrictedOperation(COMMANDTAG_LISTEN);
Async_Listen(stmt->conditionname);
}
break;
@@ -802,7 +836,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
{
UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
- CheckRestrictedOperation("UNLISTEN");
+ CheckRestrictedOperation(COMMANDTAG_UNLISTEN);
if (stmt->conditionname)
Async_Unlisten(stmt->conditionname);
else
@@ -855,7 +889,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case T_DiscardStmt:
/* should we allow DISCARD PLANS? */
- CheckRestrictedOperation("DISCARD");
+ CheckRestrictedOperation(COMMANDTAG_DISCARD);
DiscardCommand((DiscardStmt *) parsetree, isTopLevel);
break;
@@ -974,7 +1008,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecuteGrantStmt(stmt);
}
@@ -987,7 +1021,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecDropStmt(stmt, isTopLevel);
}
@@ -1000,7 +1034,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecRenameStmt(stmt);
}
@@ -1013,7 +1047,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectDependsStmt(stmt, NULL);
}
@@ -1026,7 +1060,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
}
@@ -1039,7 +1073,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterOwnerStmt(stmt);
}
@@ -1052,7 +1086,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
CommentObject(stmt);
break;
@@ -1065,7 +1099,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecSecLabelStmt(stmt);
break;
@@ -1075,7 +1109,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
break;
}
@@ -1102,7 +1136,7 @@ ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -1605,7 +1639,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateTableAsStmt:
address = ExecCreateTableAs(pstate, (CreateTableAsStmt *) parsetree,
- params, queryEnv, completionTag);
+ params, queryEnv, qc);
break;
case T_RefreshMatViewStmt:
@@ -1620,7 +1654,7 @@ ProcessUtilitySlow(ParseState *pstate,
PG_TRY();
{
address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
- queryString, params, completionTag);
+ queryString, params, qc);
}
PG_FINALLY();
{
@@ -2099,137 +2133,137 @@ UtilityContainsQuery(Node *parsetree)
*
* This covers most cases where ALTER is used with an ObjectType enum.
*/
-static const char *
+static CommandTag
AlterObjectTypeCommandTag(ObjectType objtype)
{
- const char *tag;
+ CommandTag tag;
switch (objtype)
{
case OBJECT_AGGREGATE:
- tag = "ALTER AGGREGATE";
+ tag = COMMANDTAG_ALTER_AGGREGATE;
break;
case OBJECT_ATTRIBUTE:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case OBJECT_CAST:
- tag = "ALTER CAST";
+ tag = COMMANDTAG_ALTER_CAST;
break;
case OBJECT_COLLATION:
- tag = "ALTER COLLATION";
+ tag = COMMANDTAG_ALTER_COLLATION;
break;
case OBJECT_COLUMN:
- tag = "ALTER TABLE";
+ tag = COMMANDTAG_ALTER_TABLE;
break;
case OBJECT_CONVERSION:
- tag = "ALTER CONVERSION";
+ tag = COMMANDTAG_ALTER_CONVERSION;
break;
case OBJECT_DATABASE:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case OBJECT_DOMAIN:
case OBJECT_DOMCONSTRAINT:
- tag = "ALTER DOMAIN";
+ tag = COMMANDTAG_ALTER_DOMAIN;
break;
case OBJECT_EXTENSION:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case OBJECT_FDW:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "ALTER SERVER";
+ tag = COMMANDTAG_ALTER_SERVER;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "ALTER FOREIGN TABLE";
+ tag = COMMANDTAG_ALTER_FOREIGN_TABLE;
break;
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = COMMANDTAG_ALTER_FUNCTION;
break;
case OBJECT_INDEX:
- tag = "ALTER INDEX";
+ tag = COMMANDTAG_ALTER_INDEX;
break;
case OBJECT_LANGUAGE:
- tag = "ALTER LANGUAGE";
+ tag = COMMANDTAG_ALTER_LANGUAGE;
break;
case OBJECT_LARGEOBJECT:
- tag = "ALTER LARGE OBJECT";
+ tag = COMMANDTAG_ALTER_LARGE_OBJECT;
break;
case OBJECT_OPCLASS:
- tag = "ALTER OPERATOR CLASS";
+ tag = COMMANDTAG_ALTER_OPERATOR_CLASS;
break;
case OBJECT_OPERATOR:
- tag = "ALTER OPERATOR";
+ tag = COMMANDTAG_ALTER_OPERATOR;
break;
case OBJECT_OPFAMILY:
- tag = "ALTER OPERATOR FAMILY";
+ tag = COMMANDTAG_ALTER_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "ALTER POLICY";
+ tag = COMMANDTAG_ALTER_POLICY;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = COMMANDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROLE:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = COMMANDTAG_ALTER_ROUTINE;
break;
case OBJECT_RULE:
- tag = "ALTER RULE";
+ tag = COMMANDTAG_ALTER_RULE;
break;
case OBJECT_SCHEMA:
- tag = "ALTER SCHEMA";
+ tag = COMMANDTAG_ALTER_SCHEMA;
break;
case OBJECT_SEQUENCE:
- tag = "ALTER SEQUENCE";
+ tag = COMMANDTAG_ALTER_SEQUENCE;
break;
case OBJECT_TABLE:
case OBJECT_TABCONSTRAINT:
- tag = "ALTER TABLE";
+ tag = COMMANDTAG_ALTER_TABLE;
break;
case OBJECT_TABLESPACE:
- tag = "ALTER TABLESPACE";
+ tag = COMMANDTAG_ALTER_TABLESPACE;
break;
case OBJECT_TRIGGER:
- tag = "ALTER TRIGGER";
+ tag = COMMANDTAG_ALTER_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "ALTER EVENT TRIGGER";
+ tag = COMMANDTAG_ALTER_EVENT_TRIGGER;
break;
case OBJECT_TSCONFIGURATION:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_TSDICTIONARY:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSPARSER:
- tag = "ALTER TEXT SEARCH PARSER";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSTEMPLATE:
- tag = "ALTER TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TYPE:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case OBJECT_VIEW:
- tag = "ALTER VIEW";
+ tag = COMMANDTAG_ALTER_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "ALTER MATERIALIZED VIEW";
+ tag = COMMANDTAG_ALTER_MATERIALIZED_VIEW;
break;
case OBJECT_PUBLICATION:
- tag = "ALTER PUBLICATION";
+ tag = COMMANDTAG_ALTER_PUBLICATION;
break;
case OBJECT_SUBSCRIPTION:
- tag = "ALTER SUBSCRIPTION";
+ tag = COMMANDTAG_ALTER_SUBSCRIPTION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "ALTER STATISTICS";
+ tag = COMMANDTAG_ALTER_STATISTICS;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
@@ -2238,20 +2272,17 @@ AlterObjectTypeCommandTag(ObjectType objtype)
/*
* CreateCommandTag
- * utility to get a string representation of the command operation,
+ * utility to get a CommandTag for the command operation,
* given either a raw (un-analyzed) parsetree, an analyzed Query,
* or a PlannedStmt.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
- *
- * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
- * Also, the result must point at a true constant (permanent storage).
*/
-const char *
+CommandTag
CreateCommandTag(Node *parsetree)
{
- const char *tag;
+ CommandTag tag;
switch (nodeTag(parsetree))
{
@@ -2262,19 +2293,19 @@ CreateCommandTag(Node *parsetree)
/* raw plannable queries */
case T_InsertStmt:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case T_DeleteStmt:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case T_UpdateStmt:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case T_SelectStmt:
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
/* utility statements --- same whether raw or cooked */
@@ -2285,51 +2316,51 @@ CreateCommandTag(Node *parsetree)
switch (stmt->kind)
{
case TRANS_STMT_BEGIN:
- tag = "BEGIN";
+ tag = COMMANDTAG_BEGIN;
break;
case TRANS_STMT_START:
- tag = "START TRANSACTION";
+ tag = COMMANDTAG_START_TRANSACTION;
break;
case TRANS_STMT_COMMIT:
- tag = "COMMIT";
+ tag = COMMANDTAG_COMMIT;
break;
case TRANS_STMT_ROLLBACK:
case TRANS_STMT_ROLLBACK_TO:
- tag = "ROLLBACK";
+ tag = COMMANDTAG_ROLLBACK;
break;
case TRANS_STMT_SAVEPOINT:
- tag = "SAVEPOINT";
+ tag = COMMANDTAG_SAVEPOINT;
break;
case TRANS_STMT_RELEASE:
- tag = "RELEASE";
+ tag = COMMANDTAG_RELEASE;
break;
case TRANS_STMT_PREPARE:
- tag = "PREPARE TRANSACTION";
+ tag = COMMANDTAG_PREPARE_TRANSACTION;
break;
case TRANS_STMT_COMMIT_PREPARED:
- tag = "COMMIT PREPARED";
+ tag = COMMANDTAG_COMMIT_PREPARED;
break;
case TRANS_STMT_ROLLBACK_PREPARED:
- tag = "ROLLBACK PREPARED";
+ tag = COMMANDTAG_ROLLBACK_PREPARED;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
break;
case T_DeclareCursorStmt:
- tag = "DECLARE CURSOR";
+ tag = COMMANDTAG_DECLARE_CURSOR;
break;
case T_ClosePortalStmt:
@@ -2337,9 +2368,9 @@ CreateCommandTag(Node *parsetree)
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
if (stmt->portalname == NULL)
- tag = "CLOSE CURSOR ALL";
+ tag = COMMANDTAG_CLOSE_CURSOR_ALL;
else
- tag = "CLOSE CURSOR";
+ tag = COMMANDTAG_CLOSE_CURSOR;
}
break;
@@ -2347,209 +2378,209 @@ CreateCommandTag(Node *parsetree)
{
FetchStmt *stmt = (FetchStmt *) parsetree;
- tag = (stmt->ismove) ? "MOVE" : "FETCH";
+ tag = (stmt->ismove) ? COMMANDTAG_MOVE : COMMANDTAG_FETCH;
}
break;
case T_CreateDomainStmt:
- tag = "CREATE DOMAIN";
+ tag = COMMANDTAG_CREATE_DOMAIN;
break;
case T_CreateSchemaStmt:
- tag = "CREATE SCHEMA";
+ tag = COMMANDTAG_CREATE_SCHEMA;
break;
case T_CreateStmt:
- tag = "CREATE TABLE";
+ tag = COMMANDTAG_CREATE_TABLE;
break;
case T_CreateTableSpaceStmt:
- tag = "CREATE TABLESPACE";
+ tag = COMMANDTAG_CREATE_TABLESPACE;
break;
case T_DropTableSpaceStmt:
- tag = "DROP TABLESPACE";
+ tag = COMMANDTAG_DROP_TABLESPACE;
break;
case T_AlterTableSpaceOptionsStmt:
- tag = "ALTER TABLESPACE";
+ tag = COMMANDTAG_ALTER_TABLESPACE;
break;
case T_CreateExtensionStmt:
- tag = "CREATE EXTENSION";
+ tag = COMMANDTAG_CREATE_EXTENSION;
break;
case T_AlterExtensionStmt:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case T_AlterExtensionContentsStmt:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case T_CreateFdwStmt:
- tag = "CREATE FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER;
break;
case T_AlterFdwStmt:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case T_CreateForeignServerStmt:
- tag = "CREATE SERVER";
+ tag = COMMANDTAG_CREATE_SERVER;
break;
case T_AlterForeignServerStmt:
- tag = "ALTER SERVER";
+ tag = COMMANDTAG_ALTER_SERVER;
break;
case T_CreateUserMappingStmt:
- tag = "CREATE USER MAPPING";
+ tag = COMMANDTAG_CREATE_USER_MAPPING;
break;
case T_AlterUserMappingStmt:
- tag = "ALTER USER MAPPING";
+ tag = COMMANDTAG_ALTER_USER_MAPPING;
break;
case T_DropUserMappingStmt:
- tag = "DROP USER MAPPING";
+ tag = COMMANDTAG_DROP_USER_MAPPING;
break;
case T_CreateForeignTableStmt:
- tag = "CREATE FOREIGN TABLE";
+ tag = COMMANDTAG_CREATE_FOREIGN_TABLE;
break;
case T_ImportForeignSchemaStmt:
- tag = "IMPORT FOREIGN SCHEMA";
+ tag = COMMANDTAG_IMPORT_FOREIGN_SCHEMA;
break;
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_TABLE:
- tag = "DROP TABLE";
+ tag = COMMANDTAG_DROP_TABLE;
break;
case OBJECT_SEQUENCE:
- tag = "DROP SEQUENCE";
+ tag = COMMANDTAG_DROP_SEQUENCE;
break;
case OBJECT_VIEW:
- tag = "DROP VIEW";
+ tag = COMMANDTAG_DROP_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "DROP MATERIALIZED VIEW";
+ tag = COMMANDTAG_DROP_MATERIALIZED_VIEW;
break;
case OBJECT_INDEX:
- tag = "DROP INDEX";
+ tag = COMMANDTAG_DROP_INDEX;
break;
case OBJECT_TYPE:
- tag = "DROP TYPE";
+ tag = COMMANDTAG_DROP_TYPE;
break;
case OBJECT_DOMAIN:
- tag = "DROP DOMAIN";
+ tag = COMMANDTAG_DROP_DOMAIN;
break;
case OBJECT_COLLATION:
- tag = "DROP COLLATION";
+ tag = COMMANDTAG_DROP_COLLATION;
break;
case OBJECT_CONVERSION:
- tag = "DROP CONVERSION";
+ tag = COMMANDTAG_DROP_CONVERSION;
break;
case OBJECT_SCHEMA:
- tag = "DROP SCHEMA";
+ tag = COMMANDTAG_DROP_SCHEMA;
break;
case OBJECT_TSPARSER:
- tag = "DROP TEXT SEARCH PARSER";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "DROP TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "DROP TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "DROP TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "DROP FOREIGN TABLE";
+ tag = COMMANDTAG_DROP_FOREIGN_TABLE;
break;
case OBJECT_EXTENSION:
- tag = "DROP EXTENSION";
+ tag = COMMANDTAG_DROP_EXTENSION;
break;
case OBJECT_FUNCTION:
- tag = "DROP FUNCTION";
+ tag = COMMANDTAG_DROP_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "DROP PROCEDURE";
+ tag = COMMANDTAG_DROP_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "DROP ROUTINE";
+ tag = COMMANDTAG_DROP_ROUTINE;
break;
case OBJECT_AGGREGATE:
- tag = "DROP AGGREGATE";
+ tag = COMMANDTAG_DROP_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "DROP OPERATOR";
+ tag = COMMANDTAG_DROP_OPERATOR;
break;
case OBJECT_LANGUAGE:
- tag = "DROP LANGUAGE";
+ tag = COMMANDTAG_DROP_LANGUAGE;
break;
case OBJECT_CAST:
- tag = "DROP CAST";
+ tag = COMMANDTAG_DROP_CAST;
break;
case OBJECT_TRIGGER:
- tag = "DROP TRIGGER";
+ tag = COMMANDTAG_DROP_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "DROP EVENT TRIGGER";
+ tag = COMMANDTAG_DROP_EVENT_TRIGGER;
break;
case OBJECT_RULE:
- tag = "DROP RULE";
+ tag = COMMANDTAG_DROP_RULE;
break;
case OBJECT_FDW:
- tag = "DROP FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "DROP SERVER";
+ tag = COMMANDTAG_DROP_SERVER;
break;
case OBJECT_OPCLASS:
- tag = "DROP OPERATOR CLASS";
+ tag = COMMANDTAG_DROP_OPERATOR_CLASS;
break;
case OBJECT_OPFAMILY:
- tag = "DROP OPERATOR FAMILY";
+ tag = COMMANDTAG_DROP_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "DROP POLICY";
+ tag = COMMANDTAG_DROP_POLICY;
break;
case OBJECT_TRANSFORM:
- tag = "DROP TRANSFORM";
+ tag = COMMANDTAG_DROP_TRANSFORM;
break;
case OBJECT_ACCESS_METHOD:
- tag = "DROP ACCESS METHOD";
+ tag = COMMANDTAG_DROP_ACCESS_METHOD;
break;
case OBJECT_PUBLICATION:
- tag = "DROP PUBLICATION";
+ tag = COMMANDTAG_DROP_PUBLICATION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "DROP STATISTICS";
+ tag = COMMANDTAG_DROP_STATISTICS;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_TruncateStmt:
- tag = "TRUNCATE TABLE";
+ tag = COMMANDTAG_TRUNCATE_TABLE;
break;
case T_CommentStmt:
- tag = "COMMENT";
+ tag = COMMANDTAG_COMMENT;
break;
case T_SecLabelStmt:
- tag = "SECURITY LABEL";
+ tag = COMMANDTAG_SECURITY_LABEL;
break;
case T_CopyStmt:
- tag = "COPY";
+ tag = COMMANDTAG_COPY;
break;
case T_RenameStmt:
@@ -2584,23 +2615,23 @@ CreateCommandTag(Node *parsetree)
break;
case T_AlterDomainStmt:
- tag = "ALTER DOMAIN";
+ tag = COMMANDTAG_ALTER_DOMAIN;
break;
case T_AlterFunctionStmt:
switch (((AlterFunctionStmt *) parsetree)->objtype)
{
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = COMMANDTAG_ALTER_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = COMMANDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = COMMANDTAG_ALTER_ROUTINE;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
@@ -2608,7 +2639,7 @@ CreateCommandTag(Node *parsetree)
{
GrantStmt *stmt = (GrantStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
+ tag = (stmt->is_grant) ? COMMANDTAG_GRANT : COMMANDTAG_REVOKE;
}
break;
@@ -2616,145 +2647,145 @@ CreateCommandTag(Node *parsetree)
{
GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
+ tag = (stmt->is_grant) ? COMMANDTAG_GRANT_ROLE : COMMANDTAG_REVOKE_ROLE;
}
break;
case T_AlterDefaultPrivilegesStmt:
- tag = "ALTER DEFAULT PRIVILEGES";
+ tag = COMMANDTAG_ALTER_DEFAULT_PRIVILEGES;
break;
case T_DefineStmt:
switch (((DefineStmt *) parsetree)->kind)
{
case OBJECT_AGGREGATE:
- tag = "CREATE AGGREGATE";
+ tag = COMMANDTAG_CREATE_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "CREATE OPERATOR";
+ tag = COMMANDTAG_CREATE_OPERATOR;
break;
case OBJECT_TYPE:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case OBJECT_TSPARSER:
- tag = "CREATE TEXT SEARCH PARSER";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "CREATE TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "CREATE TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "CREATE TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_COLLATION:
- tag = "CREATE COLLATION";
+ tag = COMMANDTAG_CREATE_COLLATION;
break;
case OBJECT_ACCESS_METHOD:
- tag = "CREATE ACCESS METHOD";
+ tag = COMMANDTAG_CREATE_ACCESS_METHOD;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_CompositeTypeStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_CreateEnumStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_CreateRangeStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_AlterEnumStmt:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case T_ViewStmt:
- tag = "CREATE VIEW";
+ tag = COMMANDTAG_CREATE_VIEW;
break;
case T_CreateFunctionStmt:
if (((CreateFunctionStmt *) parsetree)->is_procedure)
- tag = "CREATE PROCEDURE";
+ tag = COMMANDTAG_CREATE_PROCEDURE;
else
- tag = "CREATE FUNCTION";
+ tag = COMMANDTAG_CREATE_FUNCTION;
break;
case T_IndexStmt:
- tag = "CREATE INDEX";
+ tag = COMMANDTAG_CREATE_INDEX;
break;
case T_RuleStmt:
- tag = "CREATE RULE";
+ tag = COMMANDTAG_CREATE_RULE;
break;
case T_CreateSeqStmt:
- tag = "CREATE SEQUENCE";
+ tag = COMMANDTAG_CREATE_SEQUENCE;
break;
case T_AlterSeqStmt:
- tag = "ALTER SEQUENCE";
+ tag = COMMANDTAG_ALTER_SEQUENCE;
break;
case T_DoStmt:
- tag = "DO";
+ tag = COMMANDTAG_DO;
break;
case T_CreatedbStmt:
- tag = "CREATE DATABASE";
+ tag = COMMANDTAG_CREATE_DATABASE;
break;
case T_AlterDatabaseStmt:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case T_AlterDatabaseSetStmt:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case T_DropdbStmt:
- tag = "DROP DATABASE";
+ tag = COMMANDTAG_DROP_DATABASE;
break;
case T_NotifyStmt:
- tag = "NOTIFY";
+ tag = COMMANDTAG_NOTIFY;
break;
case T_ListenStmt:
- tag = "LISTEN";
+ tag = COMMANDTAG_LISTEN;
break;
case T_UnlistenStmt:
- tag = "UNLISTEN";
+ tag = COMMANDTAG_UNLISTEN;
break;
case T_LoadStmt:
- tag = "LOAD";
+ tag = COMMANDTAG_LOAD;
break;
case T_CallStmt:
- tag = "CALL";
+ tag = COMMANDTAG_CALL;
break;
case T_ClusterStmt:
- tag = "CLUSTER";
+ tag = COMMANDTAG_CLUSTER;
break;
case T_VacuumStmt:
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
- tag = "VACUUM";
+ tag = COMMANDTAG_VACUUM;
else
- tag = "ANALYZE";
+ tag = COMMANDTAG_ANALYZE;
break;
case T_ExplainStmt:
- tag = "EXPLAIN";
+ tag = COMMANDTAG_EXPLAIN;
break;
case T_CreateTableAsStmt:
@@ -2762,24 +2793,24 @@ CreateCommandTag(Node *parsetree)
{
case OBJECT_TABLE:
if (((CreateTableAsStmt *) parsetree)->is_select_into)
- tag = "SELECT INTO";
+ tag = COMMANDTAG_SELECT_INTO;
else
- tag = "CREATE TABLE AS";
+ tag = COMMANDTAG_CREATE_TABLE_AS;
break;
case OBJECT_MATVIEW:
- tag = "CREATE MATERIALIZED VIEW";
+ tag = COMMANDTAG_CREATE_MATERIALIZED_VIEW;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_RefreshMatViewStmt:
- tag = "REFRESH MATERIALIZED VIEW";
+ tag = COMMANDTAG_REFRESH_MATERIALIZED_VIEW;
break;
case T_AlterSystemStmt:
- tag = "ALTER SYSTEM";
+ tag = COMMANDTAG_ALTER_SYSTEM;
break;
case T_VariableSetStmt:
@@ -2789,183 +2820,183 @@ CreateCommandTag(Node *parsetree)
case VAR_SET_CURRENT:
case VAR_SET_DEFAULT:
case VAR_SET_MULTI:
- tag = "SET";
+ tag = COMMANDTAG_SET;
break;
case VAR_RESET:
case VAR_RESET_ALL:
- tag = "RESET";
+ tag = COMMANDTAG_RESET;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_VariableShowStmt:
- tag = "SHOW";
+ tag = COMMANDTAG_SHOW;
break;
case T_DiscardStmt:
switch (((DiscardStmt *) parsetree)->target)
{
case DISCARD_ALL:
- tag = "DISCARD ALL";
+ tag = COMMANDTAG_DISCARD_ALL;
break;
case DISCARD_PLANS:
- tag = "DISCARD PLANS";
+ tag = COMMANDTAG_DISCARD_PLANS;
break;
case DISCARD_TEMP:
- tag = "DISCARD TEMP";
+ tag = COMMANDTAG_DISCARD_TEMP;
break;
case DISCARD_SEQUENCES:
- tag = "DISCARD SEQUENCES";
+ tag = COMMANDTAG_DISCARD_SEQUENCES;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_CreateTransformStmt:
- tag = "CREATE TRANSFORM";
+ tag = COMMANDTAG_CREATE_TRANSFORM;
break;
case T_CreateTrigStmt:
- tag = "CREATE TRIGGER";
+ tag = COMMANDTAG_CREATE_TRIGGER;
break;
case T_CreateEventTrigStmt:
- tag = "CREATE EVENT TRIGGER";
+ tag = COMMANDTAG_CREATE_EVENT_TRIGGER;
break;
case T_AlterEventTrigStmt:
- tag = "ALTER EVENT TRIGGER";
+ tag = COMMANDTAG_ALTER_EVENT_TRIGGER;
break;
case T_CreatePLangStmt:
- tag = "CREATE LANGUAGE";
+ tag = COMMANDTAG_CREATE_LANGUAGE;
break;
case T_CreateRoleStmt:
- tag = "CREATE ROLE";
+ tag = COMMANDTAG_CREATE_ROLE;
break;
case T_AlterRoleStmt:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case T_AlterRoleSetStmt:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case T_DropRoleStmt:
- tag = "DROP ROLE";
+ tag = COMMANDTAG_DROP_ROLE;
break;
case T_DropOwnedStmt:
- tag = "DROP OWNED";
+ tag = COMMANDTAG_DROP_OWNED;
break;
case T_ReassignOwnedStmt:
- tag = "REASSIGN OWNED";
+ tag = COMMANDTAG_REASSIGN_OWNED;
break;
case T_LockStmt:
- tag = "LOCK TABLE";
+ tag = COMMANDTAG_LOCK_TABLE;
break;
case T_ConstraintsSetStmt:
- tag = "SET CONSTRAINTS";
+ tag = COMMANDTAG_SET_CONSTRAINTS;
break;
case T_CheckPointStmt:
- tag = "CHECKPOINT";
+ tag = COMMANDTAG_CHECKPOINT;
break;
case T_ReindexStmt:
- tag = "REINDEX";
+ tag = COMMANDTAG_REINDEX;
break;
case T_CreateConversionStmt:
- tag = "CREATE CONVERSION";
+ tag = COMMANDTAG_CREATE_CONVERSION;
break;
case T_CreateCastStmt:
- tag = "CREATE CAST";
+ tag = COMMANDTAG_CREATE_CAST;
break;
case T_CreateOpClassStmt:
- tag = "CREATE OPERATOR CLASS";
+ tag = COMMANDTAG_CREATE_OPERATOR_CLASS;
break;
case T_CreateOpFamilyStmt:
- tag = "CREATE OPERATOR FAMILY";
+ tag = COMMANDTAG_CREATE_OPERATOR_FAMILY;
break;
case T_AlterOpFamilyStmt:
- tag = "ALTER OPERATOR FAMILY";
+ tag = COMMANDTAG_ALTER_OPERATOR_FAMILY;
break;
case T_AlterOperatorStmt:
- tag = "ALTER OPERATOR";
+ tag = COMMANDTAG_ALTER_OPERATOR;
break;
case T_AlterTSDictionaryStmt:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case T_AlterTSConfigurationStmt:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case T_CreatePolicyStmt:
- tag = "CREATE POLICY";
+ tag = COMMANDTAG_CREATE_POLICY;
break;
case T_AlterPolicyStmt:
- tag = "ALTER POLICY";
+ tag = COMMANDTAG_ALTER_POLICY;
break;
case T_CreateAmStmt:
- tag = "CREATE ACCESS METHOD";
+ tag = COMMANDTAG_CREATE_ACCESS_METHOD;
break;
case T_CreatePublicationStmt:
- tag = "CREATE PUBLICATION";
+ tag = COMMANDTAG_CREATE_PUBLICATION;
break;
case T_AlterPublicationStmt:
- tag = "ALTER PUBLICATION";
+ tag = COMMANDTAG_ALTER_PUBLICATION;
break;
case T_CreateSubscriptionStmt:
- tag = "CREATE SUBSCRIPTION";
+ tag = COMMANDTAG_CREATE_SUBSCRIPTION;
break;
case T_AlterSubscriptionStmt:
- tag = "ALTER SUBSCRIPTION";
+ tag = COMMANDTAG_ALTER_SUBSCRIPTION;
break;
case T_DropSubscriptionStmt:
- tag = "DROP SUBSCRIPTION";
+ tag = COMMANDTAG_DROP_SUBSCRIPTION;
break;
case T_AlterCollationStmt:
- tag = "ALTER COLLATION";
+ tag = COMMANDTAG_ALTER_COLLATION;
break;
case T_PrepareStmt:
- tag = "PREPARE";
+ tag = COMMANDTAG_PREPARE;
break;
case T_ExecuteStmt:
- tag = "EXECUTE";
+ tag = COMMANDTAG_EXECUTE;
break;
case T_CreateStatsStmt:
- tag = "CREATE STATISTICS";
+ tag = COMMANDTAG_CREATE_STATISTICS;
break;
case T_AlterStatsStmt:
- tag = "ALTER STATISTICS";
+ tag = COMMANDTAG_ALTER_STATISTICS;
break;
case T_DeallocateStmt:
@@ -2973,9 +3004,9 @@ CreateCommandTag(Node *parsetree)
DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
if (stmt->name == NULL)
- tag = "DEALLOCATE ALL";
+ tag = COMMANDTAG_DEALLOCATE_ALL;
else
- tag = "DEALLOCATE";
+ tag = COMMANDTAG_DEALLOCATE;
}
break;
@@ -2999,33 +3030,33 @@ CreateCommandTag(Node *parsetree)
switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = COMMANDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = COMMANDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
}
}
else
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3033,7 +3064,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
@@ -3059,33 +3090,33 @@ CreateCommandTag(Node *parsetree)
switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = COMMANDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = COMMANDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
else
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3093,7 +3124,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
@@ -3102,7 +3133,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c
index 33272f8030..37e3cc70f8 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/txid.c
@@ -425,7 +425,7 @@ txid_current(PG_FUNCTION_ARGS)
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
+ PreventCommandStrDuringRecovery("txid_current()");
load_xid_epoch(&state);
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index c47be0ba4c..53401bd4e9 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -163,7 +163,7 @@ InitPlanCache(void)
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
MemoryContext source_context;
@@ -246,7 +246,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
CachedPlanSource *
CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index 2397fc2453..e67b8ab4fe 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = \
+ commandtag.o \
guc.o \
help_config.o \
pg_config.o \
diff --git a/src/backend/utils/misc/commandtag.c b/src/backend/utils/misc/commandtag.c
new file mode 100644
index 0000000000..aac13fbe5e
--- /dev/null
+++ b/src/backend/utils/misc/commandtag.c
@@ -0,0 +1,320 @@
+/*-------------------------------------------------------------------------
+ *
+ * commandtag.c
+ * Data and routines for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/commandtag.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "c.h"
+#include "utils/commandtag.h"
+
+static const char * tag_names[] = {
+ "???",
+ "ALTER ACCESS METHOD",
+ "ALTER AGGREGATE",
+ "ALTER CAST",
+ "ALTER COLLATION",
+ "ALTER CONSTRAINT",
+ "ALTER CONVERSION",
+ "ALTER DATABASE",
+ "ALTER DEFAULT PRIVILEGES",
+ "ALTER DOMAIN",
+ "ALTER EVENT TRIGGER",
+ "ALTER EXTENSION",
+ "ALTER FOREIGN DATA WRAPPER",
+ "ALTER FOREIGN TABLE",
+ "ALTER FUNCTION",
+ "ALTER INDEX",
+ "ALTER LANGUAGE",
+ "ALTER LARGE OBJECT",
+ "ALTER MATERIALIZED VIEW",
+ "ALTER OPERATOR",
+ "ALTER OPERATOR CLASS",
+ "ALTER OPERATOR FAMILY",
+ "ALTER POLICY",
+ "ALTER PROCEDURE",
+ "ALTER PUBLICATION",
+ "ALTER ROLE",
+ "ALTER ROUTINE",
+ "ALTER RULE",
+ "ALTER SCHEMA",
+ "ALTER SEQUENCE",
+ "ALTER SERVER",
+ "ALTER STATISTICS",
+ "ALTER SUBSCRIPTION",
+ "ALTER SYSTEM",
+ "ALTER TABLE",
+ "ALTER TABLESPACE",
+ "ALTER TEXT SEARCH CONFIGURATION",
+ "ALTER TEXT SEARCH DICTIONARY",
+ "ALTER TEXT SEARCH PARSER",
+ "ALTER TEXT SEARCH TEMPLATE",
+ "ALTER TRANSFORM",
+ "ALTER TRIGGER",
+ "ALTER TYPE",
+ "ALTER USER MAPPING",
+ "ALTER VIEW",
+ "ANALYZE",
+ "BEGIN",
+ "CALL",
+ "CHECKPOINT",
+ "CLOSE",
+ "CLOSE CURSOR",
+ "CLOSE CURSOR ALL",
+ "CLUSTER",
+ "COMMENT",
+ "COMMIT",
+ "COMMIT PREPARED",
+ "COPY",
+ "COPY FROM",
+ "CREATE ACCESS METHOD",
+ "CREATE AGGREGATE",
+ "CREATE CAST",
+ "CREATE COLLATION",
+ "CREATE CONSTRAINT",
+ "CREATE CONVERSION",
+ "CREATE DATABASE",
+ "CREATE DOMAIN",
+ "CREATE EVENT TRIGGER",
+ "CREATE EXTENSION",
+ "CREATE FOREIGN DATA WRAPPER",
+ "CREATE FOREIGN TABLE",
+ "CREATE FUNCTION",
+ "CREATE INDEX",
+ "CREATE LANGUAGE",
+ "CREATE MATERIALIZED VIEW",
+ "CREATE OPERATOR",
+ "CREATE OPERATOR CLASS",
+ "CREATE OPERATOR FAMILY",
+ "CREATE POLICY",
+ "CREATE PROCEDURE",
+ "CREATE PUBLICATION",
+ "CREATE ROLE",
+ "CREATE ROUTINE",
+ "CREATE RULE",
+ "CREATE SCHEMA",
+ "CREATE SEQUENCE",
+ "CREATE SERVER",
+ "CREATE STATISTICS",
+ "CREATE SUBSCRIPTION",
+ "CREATE TABLE",
+ "CREATE TABLE AS",
+ "CREATE TABLESPACE",
+ "CREATE TEXT SEARCH CONFIGURATION",
+ "CREATE TEXT SEARCH DICTIONARY",
+ "CREATE TEXT SEARCH PARSER",
+ "CREATE TEXT SEARCH TEMPLATE",
+ "CREATE TRANSFORM",
+ "CREATE TRIGGER",
+ "CREATE TYPE",
+ "CREATE USER MAPPING",
+ "CREATE VIEW",
+ "DEALLOCATE",
+ "DEALLOCATE ALL",
+ "DECLARE CURSOR",
+ "DELETE",
+ "DISCARD",
+ "DISCARD ALL",
+ "DISCARD PLANS",
+ "DISCARD SEQUENCES",
+ "DISCARD TEMP",
+ "DO",
+ "DROP ACCESS METHOD",
+ "DROP AGGREGATE",
+ "DROP CAST",
+ "DROP COLLATION",
+ "DROP CONSTRAINT",
+ "DROP CONVERSION",
+ "DROP DATABASE",
+ "DROP DOMAIN",
+ "DROP EVENT TRIGGER",
+ "DROP EXTENSION",
+ "DROP FOREIGN DATA WRAPPER",
+ "DROP FOREIGN TABLE",
+ "DROP FUNCTION",
+ "DROP INDEX",
+ "DROP LANGUAGE",
+ "DROP MATERIALIZED VIEW",
+ "DROP OPERATOR",
+ "DROP OPERATOR CLASS",
+ "DROP OPERATOR FAMILY",
+ "DROP OWNED",
+ "DROP POLICY",
+ "DROP PROCEDURE",
+ "DROP PUBLICATION",
+ "DROP REPLICATION SLOT",
+ "DROP ROLE",
+ "DROP ROUTINE",
+ "DROP RULE",
+ "DROP SCHEMA",
+ "DROP SEQUENCE",
+ "DROP SERVER",
+ "DROP STATISTICS",
+ "DROP SUBSCRIPTION",
+ "DROP TABLE",
+ "DROP TABLESPACE",
+ "DROP TEXT SEARCH CONFIGURATION",
+ "DROP TEXT SEARCH DICTIONARY",
+ "DROP TEXT SEARCH PARSER",
+ "DROP TEXT SEARCH TEMPLATE",
+ "DROP TRANSFORM",
+ "DROP TRIGGER",
+ "DROP TYPE",
+ "DROP USER MAPPING",
+ "DROP VIEW",
+ "EXECUTE",
+ "EXPLAIN",
+ "FETCH",
+ "GRANT",
+ "GRANT ROLE",
+ "IMPORT FOREIGN SCHEMA",
+ "INSERT",
+ "LISTEN",
+ "LOAD",
+ "LOCK TABLE",
+ "MOVE",
+ "NOTIFY",
+ "PREPARE",
+ "PREPARE TRANSACTION",
+ "REASSIGN OWNED",
+ "REFRESH MATERIALIZED VIEW",
+ "REINDEX",
+ "RELEASE",
+ "RESET",
+ "REVOKE",
+ "REVOKE ROLE",
+ "ROLLBACK",
+ "ROLLBACK PREPARED",
+ "SAVEPOINT",
+ "SECURITY LABEL",
+ "SELECT",
+ "SELECT FOR KEY SHARE",
+ "SELECT FOR NO KEY UPDATE",
+ "SELECT FOR SHARE",
+ "SELECT FOR UPDATE",
+ "SELECT INTO",
+ "SET",
+ "SET CONSTRAINTS",
+ "SHOW",
+ "START TRANSACTION",
+ "TRUNCATE TABLE",
+ "UNLISTEN",
+ "UPDATE",
+ "VACUUM"
+};
+
+#ifdef COMMANDTAG_CHECKING
+static bool CheckCommandTagEnum(void);
+#endif
+
+static const unsigned int tag_name_length = lengthof(tag_names);
+
+#ifdef COMMANDTAG_CHECKING
+static bool commandtags_have_been_checked = false;
+static inline void
+OPTIONALLY_CHECK_COMMAND_TAGS()
+{
+ if (!commandtags_have_been_checked)
+ {
+ Assert(CheckCommandTagEnum());
+ commandtags_have_been_checked = true;
+ }
+}
+#else
+#define OPTIONALLY_CHECK_COMMAND_TAGS()
+#endif
+
+/*
+ * Search CommandTag by name
+ *
+ * Returns CommandTag, or COMMANDTAG_UNKNOWN if not recognized
+ */
+CommandTag
+GetCommandTagEnum(const char *commandname)
+{
+ const char **base, **last, **position;
+ int result;
+
+ OPTIONALLY_CHECK_COMMAND_TAGS();
+ if (commandname == NULL || *commandname == '\0')
+ return COMMANDTAG_UNKNOWN;
+
+ base = tag_names;
+ last = tag_names + tag_name_length - 1;
+ while (last >= base)
+ {
+ position = base + ((last - base) >> 1);
+ result = pg_strcasecmp(commandname, *position);
+ if (result == 0)
+ return (CommandTag) (position - tag_names);
+ else if (result < 0)
+ last = position - 1;
+ else
+ base = position + 1;
+ }
+ return COMMANDTAG_UNKNOWN;
+}
+
+const char *
+GetCommandTagName(CommandTag commandTag)
+{
+ OPTIONALLY_CHECK_COMMAND_TAGS();
+ if (PG_VALID_COMMANDTAG(commandTag))
+ return tag_names[commandTag];
+ return "???";
+}
+
+#ifdef COMMANDTAG_CHECKING
+bool
+CheckCommandTagEnum()
+{
+ CommandTag i, j;
+
+ if (FIRST_COMMAND_TAG < 0 || LAST_COMMAND_TAG < 0 || LAST_COMMAND_TAG < FIRST_COMMAND_TAG)
+ {
+ elog(ERROR, "FIRST_COMMAND_TAG (%u), LAST_COMMAND_TAG (%u) not reasonable",
+ (unsigned int) FIRST_COMMAND_TAG, (unsigned int) LAST_COMMAND_TAG);
+ return false;
+ }
+ if (FIRST_COMMAND_TAG != (CommandTag)0)
+ {
+ elog(ERROR, "FIRST_COMMAND_TAG (%u) != 0", (unsigned int) FIRST_COMMAND_TAG);
+ return false;
+ }
+ if (LAST_COMMAND_TAG != (CommandTag)(tag_name_length - 1))
+ {
+ elog(ERROR, "LAST_COMMAND_TAG (%u) != tag_name_length (%u)",
+ (unsigned int) LAST_COMMAND_TAG, (unsigned int) tag_name_length);
+ return false;
+ }
+
+ for (i = FIRST_COMMAND_TAG; i < LAST_COMMAND_TAG; i++)
+ {
+ for (j = i+1; j < LAST_COMMAND_TAG; j++)
+ {
+ int cmp = strcmp(tag_names[i], tag_names[j]);
+ if (cmp == 0)
+ {
+ elog(ERROR, "Found duplicate tag_name: \"%s\"",
+ tag_names[i]);
+ return false;
+ }
+ if (cmp > 0)
+ {
+ elog(ERROR, "Found commandnames out of order: \"%s\" before \"%s\"",
+ tag_names[i], tag_names[j]);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+#endif
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index b675575c31..b6328a049b 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -281,7 +281,7 @@ void
PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan)
{
@@ -289,10 +289,13 @@ PortalDefineQuery(Portal portal,
AssertState(portal->status == PORTAL_NEW);
AssertArg(sourceText != NULL);
- AssertArg(commandTag != NULL || stmts == NIL);
+ AssertArg(commandTag != COMMANDTAG_UNKNOWN || stmts == NIL);
portal->prepStmtName = prepStmtName;
portal->sourceText = sourceText;
+ portal->qcdata.commandTag = commandTag;
+ portal->qcdata.nprocessed = 0;
+ portal->qcdata.display_format = DISPLAYFORMAT_PLAIN;
portal->commandTag = commandTag;
portal->stmts = stmts;
portal->cplan = cplan;
diff --git a/src/include/commands/createas.h b/src/include/commands/createas.h
index 7743851a38..6e6bb8e567 100644
--- a/src/include/commands/createas.h
+++ b/src/include/commands/createas.h
@@ -22,7 +22,7 @@
extern ObjectAddress ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
- ParamListInfo params, QueryEnvironment *queryEnv, char *completionTag);
+ ParamListInfo params, QueryEnvironment *queryEnv, QueryCompletionData *qc);
extern int GetIntoRelEFlags(IntoClause *intoClause);
diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h
index 6bdb7ca258..12147a8339 100644
--- a/src/include/commands/matview.h
+++ b/src/include/commands/matview.h
@@ -24,7 +24,7 @@
extern void SetMatViewPopulatedState(Relation relation, bool newstate);
extern ObjectAddress ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag);
+ ParamListInfo params, QueryCompletionData *qc);
extern DestReceiver *CreateTransientRelDestReceiver(Oid oid);
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index 4ecc1a2ecd..667cae0a7a 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -23,7 +23,7 @@ extern void PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, Para
bool isTopLevel);
extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
- char *completionTag);
+ QueryCompletionData *qc);
extern void PerformPortalClose(const char *name);
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index a0509e1f33..b1ea4e837a 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -40,7 +40,7 @@ extern void PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
extern void ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
extern void DeallocateQuery(DeallocateStmt *stmt);
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into,
ExplainState *es, const char *queryString,
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 62d64aa0a1..afaf098be3 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -27,6 +27,7 @@
#include "datatype/timestamp.h" /* for TimestampTz */
#include "pgtime.h" /* for pg_time_t */
+#include "utils/commandtag.h" /* for CommandTag enum */
#define InvalidPid (-1)
@@ -280,9 +281,12 @@ extern void check_stack_depth(void);
extern bool stack_is_too_deep(void);
/* in tcop/utility.c */
-extern void PreventCommandIfReadOnly(const char *cmdname);
-extern void PreventCommandIfParallelMode(const char *cmdname);
-extern void PreventCommandDuringRecovery(const char *cmdname);
+extern void PreventCommandStrIfReadOnly(const char *commandstr);
+extern void PreventCommandStrIfParallelMode(const char *commandstr);
+extern void PreventCommandStrDuringRecovery(const char *commandstr);
+extern void PreventCommandIfReadOnly(CommandTag commandTag);
+extern void PreventCommandIfParallelMode(CommandTag commandTag);
+extern void PreventCommandDuringRecovery(CommandTag commandTag);
/* in utils/misc/guc.c */
extern int trace_recovery_messages;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index da0706add5..2c3ae6b603 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -27,6 +27,7 @@
#include "nodes/primnodes.h"
#include "nodes/value.h"
#include "partitioning/partdefs.h"
+#include "utils/commandtag.h"
typedef enum OverridingKind
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 35bce731a1..29bf9fbd2a 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -68,6 +68,8 @@
#define DEST_H
#include "executor/tuptable.h"
+#include "utils/commandtag.h"
+#include "utils/querycompletion.h"
/* buffer size to use for command completion tags */
@@ -134,9 +136,9 @@ extern PGDLLIMPORT DestReceiver *None_Receiver; /* permanent receiver for
/* The primary destination management functions */
-extern void BeginCommand(const char *commandTag, CommandDest dest);
+extern void BeginCommand(CommandTag commandTag, CommandDest dest);
extern DestReceiver *CreateDestReceiver(CommandDest dest);
-extern void EndCommand(const char *commandTag, CommandDest dest);
+extern void EndCommand(const QueryCompletionData *qc, CommandDest dest);
/* Additional functions that go with destination management, more or less. */
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index 4ad6324e2d..457ba3f415 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.h
@@ -35,7 +35,7 @@ extern void PortalSetResultFormat(Portal portal, int nFormats,
extern bool PortalRun(Portal portal, long count, bool isTopLevel,
bool run_once, DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletionData *qc);
extern uint64 PortalRunFetch(Portal portal,
FetchDirection fdirection,
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index a551e08cb8..90d28949d0 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -15,6 +15,7 @@
#define UTILITY_H
#include "tcop/tcopprot.h"
+#include "utils/querycompletion.h"
typedef enum
{
@@ -71,17 +72,17 @@ typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
const char *queryString, ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
extern void ProcessUtilityForAlterTable(Node *stmt,
AlterTableUtilityContext *context);
@@ -92,7 +93,7 @@ extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
extern Query *UtilityContainsQuery(Node *parsetree);
-extern const char *CreateCommandTag(Node *parsetree);
+extern CommandTag CreateCommandTag(Node *parsetree);
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
diff --git a/src/include/utils/commandtag.h b/src/include/utils/commandtag.h
new file mode 100644
index 0000000000..ab69c92d0a
--- /dev/null
+++ b/src/include/utils/commandtag.h
@@ -0,0 +1,248 @@
+/*-------------------------------------------------------------------------
+ *
+ * commandtag.h
+ * Declarations for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/commandtag.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMMANDTAG_H
+#define COMMANDTAG_H
+
+/*
+ * BEWARE: These are in sorted order, but ordered by their printed
+ * values in the tag_name list (see common/commandtag.c).
+ * In particular it matters because the sort ordering changes
+ * when you replace a space with an underscore. To wit:
+ *
+ * "CREATE TABLE"
+ * "CREATE TABLE AS"
+ * "CREATE TABLESPACE"
+ *
+ * but...
+ *
+ * CREATE_TABLE
+ * CREATE_TABLESPACE
+ * CREATE_TABLE_AS
+ *
+ * It also matters that COMMANDTAG_UNKNOWN is written "???".
+ *
+ * If you add a value here, add it in common/commandtag.c also, and
+ * be careful to get the ordering right. You can build with
+ * COMMANDTAG_CHECKING to have this automatically checked
+ * at runtime, but that adds considerable overhead, so do so sparingly.
+ */
+typedef enum CommandTag
+{
+#define FIRST_COMMAND_TAG COMMANDTAG_UNKNOWN
+ COMMANDTAG_UNKNOWN,
+ COMMANDTAG_ALTER_ACCESS_METHOD,
+ COMMANDTAG_ALTER_AGGREGATE,
+ COMMANDTAG_ALTER_CAST,
+ COMMANDTAG_ALTER_COLLATION,
+ COMMANDTAG_ALTER_CONSTRAINT,
+ COMMANDTAG_ALTER_CONVERSION,
+ COMMANDTAG_ALTER_DATABASE,
+ COMMANDTAG_ALTER_DEFAULT_PRIVILEGES,
+ COMMANDTAG_ALTER_DOMAIN,
+ COMMANDTAG_ALTER_EVENT_TRIGGER,
+ COMMANDTAG_ALTER_EXTENSION,
+ COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER,
+ COMMANDTAG_ALTER_FOREIGN_TABLE,
+ COMMANDTAG_ALTER_FUNCTION,
+ COMMANDTAG_ALTER_INDEX,
+ COMMANDTAG_ALTER_LANGUAGE,
+ COMMANDTAG_ALTER_LARGE_OBJECT,
+ COMMANDTAG_ALTER_MATERIALIZED_VIEW,
+ COMMANDTAG_ALTER_OPERATOR,
+ COMMANDTAG_ALTER_OPERATOR_CLASS,
+ COMMANDTAG_ALTER_OPERATOR_FAMILY,
+ COMMANDTAG_ALTER_POLICY,
+ COMMANDTAG_ALTER_PROCEDURE,
+ COMMANDTAG_ALTER_PUBLICATION,
+ COMMANDTAG_ALTER_ROLE,
+ COMMANDTAG_ALTER_ROUTINE,
+ COMMANDTAG_ALTER_RULE,
+ COMMANDTAG_ALTER_SCHEMA,
+ COMMANDTAG_ALTER_SEQUENCE,
+ COMMANDTAG_ALTER_SERVER,
+ COMMANDTAG_ALTER_STATISTICS,
+ COMMANDTAG_ALTER_SUBSCRIPTION,
+ COMMANDTAG_ALTER_SYSTEM,
+ COMMANDTAG_ALTER_TABLE,
+ COMMANDTAG_ALTER_TABLESPACE,
+ COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION,
+ COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY,
+ COMMANDTAG_ALTER_TEXT_SEARCH_PARSER,
+ COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE,
+ COMMANDTAG_ALTER_TRANSFORM,
+ COMMANDTAG_ALTER_TRIGGER,
+ COMMANDTAG_ALTER_TYPE,
+ COMMANDTAG_ALTER_USER_MAPPING,
+ COMMANDTAG_ALTER_VIEW,
+ COMMANDTAG_ANALYZE,
+ COMMANDTAG_BEGIN,
+ COMMANDTAG_CALL,
+ COMMANDTAG_CHECKPOINT,
+ COMMANDTAG_CLOSE,
+ COMMANDTAG_CLOSE_CURSOR,
+ COMMANDTAG_CLOSE_CURSOR_ALL,
+ COMMANDTAG_CLUSTER,
+ COMMANDTAG_COMMENT,
+ COMMANDTAG_COMMIT,
+ COMMANDTAG_COMMIT_PREPARED,
+ COMMANDTAG_COPY,
+ COMMANDTAG_COPY_FROM,
+ COMMANDTAG_CREATE_ACCESS_METHOD,
+ COMMANDTAG_CREATE_AGGREGATE,
+ COMMANDTAG_CREATE_CAST,
+ COMMANDTAG_CREATE_COLLATION,
+ COMMANDTAG_CREATE_CONSTRAINT,
+ COMMANDTAG_CREATE_CONVERSION,
+ COMMANDTAG_CREATE_DATABASE,
+ COMMANDTAG_CREATE_DOMAIN,
+ COMMANDTAG_CREATE_EVENT_TRIGGER,
+ COMMANDTAG_CREATE_EXTENSION,
+ COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER,
+ COMMANDTAG_CREATE_FOREIGN_TABLE,
+ COMMANDTAG_CREATE_FUNCTION,
+ COMMANDTAG_CREATE_INDEX,
+ COMMANDTAG_CREATE_LANGUAGE,
+ COMMANDTAG_CREATE_MATERIALIZED_VIEW,
+ COMMANDTAG_CREATE_OPERATOR,
+ COMMANDTAG_CREATE_OPERATOR_CLASS,
+ COMMANDTAG_CREATE_OPERATOR_FAMILY,
+ COMMANDTAG_CREATE_POLICY,
+ COMMANDTAG_CREATE_PROCEDURE,
+ COMMANDTAG_CREATE_PUBLICATION,
+ COMMANDTAG_CREATE_ROLE,
+ COMMANDTAG_CREATE_ROUTINE,
+ COMMANDTAG_CREATE_RULE,
+ COMMANDTAG_CREATE_SCHEMA,
+ COMMANDTAG_CREATE_SEQUENCE,
+ COMMANDTAG_CREATE_SERVER,
+ COMMANDTAG_CREATE_STATISTICS,
+ COMMANDTAG_CREATE_SUBSCRIPTION,
+ COMMANDTAG_CREATE_TABLE,
+ COMMANDTAG_CREATE_TABLE_AS,
+ COMMANDTAG_CREATE_TABLESPACE,
+ COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION,
+ COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY,
+ COMMANDTAG_CREATE_TEXT_SEARCH_PARSER,
+ COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE,
+ COMMANDTAG_CREATE_TRANSFORM,
+ COMMANDTAG_CREATE_TRIGGER,
+ COMMANDTAG_CREATE_TYPE,
+ COMMANDTAG_CREATE_USER_MAPPING,
+ COMMANDTAG_CREATE_VIEW,
+ COMMANDTAG_DEALLOCATE,
+ COMMANDTAG_DEALLOCATE_ALL,
+ COMMANDTAG_DECLARE_CURSOR,
+ COMMANDTAG_DELETE,
+ COMMANDTAG_DISCARD,
+ COMMANDTAG_DISCARD_ALL,
+ COMMANDTAG_DISCARD_PLANS,
+ COMMANDTAG_DISCARD_SEQUENCES,
+ COMMANDTAG_DISCARD_TEMP,
+ COMMANDTAG_DO,
+ COMMANDTAG_DROP_ACCESS_METHOD,
+ COMMANDTAG_DROP_AGGREGATE,
+ COMMANDTAG_DROP_CAST,
+ COMMANDTAG_DROP_COLLATION,
+ COMMANDTAG_DROP_CONSTRAINT,
+ COMMANDTAG_DROP_CONVERSION,
+ COMMANDTAG_DROP_DATABASE,
+ COMMANDTAG_DROP_DOMAIN,
+ COMMANDTAG_DROP_EVENT_TRIGGER,
+ COMMANDTAG_DROP_EXTENSION,
+ COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER,
+ COMMANDTAG_DROP_FOREIGN_TABLE,
+ COMMANDTAG_DROP_FUNCTION,
+ COMMANDTAG_DROP_INDEX,
+ COMMANDTAG_DROP_LANGUAGE,
+ COMMANDTAG_DROP_MATERIALIZED_VIEW,
+ COMMANDTAG_DROP_OPERATOR,
+ COMMANDTAG_DROP_OPERATOR_CLASS,
+ COMMANDTAG_DROP_OPERATOR_FAMILY,
+ COMMANDTAG_DROP_OWNED,
+ COMMANDTAG_DROP_POLICY,
+ COMMANDTAG_DROP_PROCEDURE,
+ COMMANDTAG_DROP_PUBLICATION,
+ COMMANDTAG_DROP_REPLICATION_SLOT,
+ COMMANDTAG_DROP_ROLE,
+ COMMANDTAG_DROP_ROUTINE,
+ COMMANDTAG_DROP_RULE,
+ COMMANDTAG_DROP_SCHEMA,
+ COMMANDTAG_DROP_SEQUENCE,
+ COMMANDTAG_DROP_SERVER,
+ COMMANDTAG_DROP_STATISTICS,
+ COMMANDTAG_DROP_SUBSCRIPTION,
+ COMMANDTAG_DROP_TABLE,
+ COMMANDTAG_DROP_TABLESPACE,
+ COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION,
+ COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY,
+ COMMANDTAG_DROP_TEXT_SEARCH_PARSER,
+ COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE,
+ COMMANDTAG_DROP_TRANSFORM,
+ COMMANDTAG_DROP_TRIGGER,
+ COMMANDTAG_DROP_TYPE,
+ COMMANDTAG_DROP_USER_MAPPING,
+ COMMANDTAG_DROP_VIEW,
+ COMMANDTAG_EXECUTE,
+ COMMANDTAG_EXPLAIN,
+ COMMANDTAG_FETCH,
+ COMMANDTAG_GRANT,
+ COMMANDTAG_GRANT_ROLE,
+ COMMANDTAG_IMPORT_FOREIGN_SCHEMA,
+ COMMANDTAG_INSERT,
+ COMMANDTAG_LISTEN,
+ COMMANDTAG_LOAD,
+ COMMANDTAG_LOCK_TABLE,
+ COMMANDTAG_MOVE,
+ COMMANDTAG_NOTIFY,
+ COMMANDTAG_PREPARE,
+ COMMANDTAG_PREPARE_TRANSACTION,
+ COMMANDTAG_REASSIGN_OWNED,
+ COMMANDTAG_REFRESH_MATERIALIZED_VIEW,
+ COMMANDTAG_REINDEX,
+ COMMANDTAG_RELEASE,
+ COMMANDTAG_RESET,
+ COMMANDTAG_REVOKE,
+ COMMANDTAG_REVOKE_ROLE,
+ COMMANDTAG_ROLLBACK,
+ COMMANDTAG_ROLLBACK_PREPARED,
+ COMMANDTAG_SAVEPOINT,
+ COMMANDTAG_SECURITY_LABEL,
+ COMMANDTAG_SELECT,
+ COMMANDTAG_SELECT_FOR_KEY_SHARE,
+ COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE,
+ COMMANDTAG_SELECT_FOR_SHARE,
+ COMMANDTAG_SELECT_FOR_UPDATE,
+ COMMANDTAG_SELECT_INTO,
+ COMMANDTAG_SET,
+ COMMANDTAG_SET_CONSTRAINTS,
+ COMMANDTAG_SHOW,
+ COMMANDTAG_START_TRANSACTION,
+ COMMANDTAG_TRUNCATE_TABLE,
+ COMMANDTAG_UNLISTEN,
+ COMMANDTAG_UPDATE,
+ COMMANDTAG_VACUUM
+#define LAST_COMMAND_TAG COMMANDTAG_VACUUM
+} CommandTag;
+
+static inline bool
+PG_VALID_COMMANDTAG(CommandTag commandTag)
+{
+ return (commandTag >= FIRST_COMMAND_TAG &&
+ commandTag <= LAST_COMMAND_TAG);
+}
+
+extern const char *GetCommandTagName(CommandTag commandTag);
+extern CommandTag GetCommandTagEnum(const char *tagname);
+
+#endif /* COMMANDTAG_H */
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index e48661ebec..04b02c083b 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -95,7 +95,7 @@ typedef struct CachedPlanSource
int magic; /* should equal CACHEDPLANSOURCE_MAGIC */
struct RawStmt *raw_parse_tree; /* output of raw_parser(), or NULL */
const char *query_string; /* source text of query */
- const char *commandTag; /* command tag (a constant!), or NULL */
+ CommandTag commandTag;
Oid *param_types; /* array of parameter type OIDs, or NULL */
int num_params; /* length of param_types array */
ParserSetupHook parserSetup; /* alternative parameter spec method */
@@ -186,10 +186,10 @@ extern void ResetPlanCache(void);
extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern void CompleteCachedPlan(CachedPlanSource *plansource,
List *querytree_list,
MemoryContext querytree_context,
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 0b69433722..2365883acd 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -48,6 +48,7 @@
#include "datatype/timestamp.h"
#include "executor/execdesc.h"
+#include "utils/commandtag.h"
#include "utils/plancache.h"
#include "utils/resowner.h"
@@ -132,7 +133,8 @@ typedef struct PortalData
/* The query or queries the portal will execute */
const char *sourceText; /* text of query (as of 8.4, never NULL) */
- const char *commandTag; /* command tag for original query */
+ CommandTag commandTag; /* command tag for original query */
+ QueryCompletionData qcdata; /* command completion data for executed query */
List *stmts; /* list of PlannedStmts */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
@@ -227,7 +229,7 @@ extern Portal GetPortalByName(const char *name);
extern void PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan);
extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
diff --git a/src/include/utils/querycompletion.h b/src/include/utils/querycompletion.h
new file mode 100644
index 0000000000..b666e1fdc3
--- /dev/null
+++ b/src/include/utils/querycompletion.h
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * queryenvironment.h
+ * Access to functions to mutate the query environment and retrieve the
+ * actual data related to entries (if any).
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/queryenvironment.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef QUERYCOMPLETION_H
+#define QUERYCOMPLETION_H
+
+#include "utils/commandtag.h"
+
+typedef enum QueryCompletionDisplayFormat
+{
+ DISPLAYFORMAT_PLAIN,
+ DISPLAYFORMAT_NPROCESSED,
+ DISPLAYFORMAT_LAST_OID
+} QueryCompletionDisplayFormat;
+
+typedef struct QueryCompletionData
+{
+ CommandTag commandTag;
+ uint64 nprocessed;
+ /* Wire-protocol backwards-compatibility hack */
+ QueryCompletionDisplayFormat display_format;
+} QueryCompletionData;
+
+typedef QueryCompletionData *QueryCompletion;
+
+static inline void
+InitializeQC(QueryCompletionData *qc)
+{
+ qc->commandTag = COMMANDTAG_UNKNOWN;
+ qc->nprocessed = 0;
+ qc->display_format = DISPLAYFORMAT_PLAIN;
+}
+
+static inline void
+SetQC(QueryCompletionData *qc, CommandTag commandTag, uint64 nprocessed, QueryCompletionDisplayFormat display_format)
+{
+ qc->commandTag = commandTag;
+ qc->nprocessed = nprocessed;
+ qc->display_format = display_format;
+}
+
+static inline void
+CopyQC(QueryCompletionData *dst, const QueryCompletionData *src)
+{
+ dst->commandTag = src->commandTag;
+ dst->nprocessed = src->nprocessed;
+ dst->display_format = src->display_format;
+}
+
+#endif /* QUERYCOMPLETION_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 5acf604f63..8a0f31b4b8 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4115,10 +4115,9 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
* tree(s), since those are the result of rewriting and could have
* been transmogrified into something else entirely.
*/
- if (plansource->commandTag &&
- (strcmp(plansource->commandTag, "INSERT") == 0 ||
- strcmp(plansource->commandTag, "UPDATE") == 0 ||
- strcmp(plansource->commandTag, "DELETE") == 0))
+ if (plansource->commandTag == COMMANDTAG_INSERT ||
+ plansource->commandTag == COMMANDTAG_UPDATE ||
+ plansource->commandTag == COMMANDTAG_DELETE)
{
stmt->mod_stmt = true;
break;
diff --git a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
index e1629ec618..d10c465a24 100644
--- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
+++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
@@ -74,7 +74,7 @@ get_command_tag(PG_FUNCTION_ARGS)
if (!cmd->parsetree)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(cstring_to_text(CreateCommandTag(cmd->parsetree)));
+ PG_RETURN_TEXT_P(cstring_to_text(GetCommandTagName(CreateCommandTag(cmd->parsetree))));
}
/*
--
2.21.1 (Apple Git-122.3)
v2-0002-Using-a-Bitmapset-of-tags-rather-than-a-string-ar.patchapplication/octet-stream; name=v2-0002-Using-a-Bitmapset-of-tags-rather-than-a-string-ar.patch; x-unix-mode=0644Download
From a70b0cadc1142e92b2354a0ca3cd47aaeb0c148e Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Tue, 4 Feb 2020 12:25:05 -0800
Subject: [PATCH v2 2/3] Using a Bitmapset of tags rather than a string array.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
EventTriggerCacheItem no longer holds an array of palloc’d tag strings
in sorted order, but rather just a Bitmapset over the CommandTags. This
makes the code a little simpler and easier to read, in my opinion. In
filter_event_trigger, rather than running bsearch through a sorted array
of strings, it just runs bms_is_member.
---
src/backend/commands/event_trigger.c | 12 +++++-------
src/backend/utils/cache/evtcache.c | 27 +++++++++++++--------------
src/include/commands/event_trigger.h | 2 +-
src/include/utils/evtcache.h | 3 +--
src/pl/plpgsql/src/pl_exec.c | 2 +-
5 files changed, 21 insertions(+), 25 deletions(-)
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 6cd9437367..96f3a06ff3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -820,7 +820,7 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
* tags matching.
*/
static bool
-filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
+filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
{
/*
* Filter by session replication role, knowing that we never see disabled
@@ -838,9 +838,7 @@ filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
}
/* Filter by tags, if any were specified. */
- if (item->ntags != 0 && bsearch(tag, item->tag,
- item->ntags, sizeof(char *),
- pg_qsort_strcmp) == NULL)
+ if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
return false;
/* if we reach that point, we're not filtering out this item */
@@ -857,7 +855,7 @@ EventTriggerCommonSetup(Node *parsetree,
EventTriggerEvent event, const char *eventstr,
EventTriggerData *trigdata)
{
- const char *tag;
+ CommandTag tag;
List *cachelist;
ListCell *lc;
List *runlist = NIL;
@@ -902,7 +900,7 @@ EventTriggerCommonSetup(Node *parsetree,
return NIL;
/* Get the command tag. */
- tag = GetCommandTagName(CreateCommandTag(parsetree));
+ tag = CreateCommandTag(parsetree);
/*
* Filter list of event triggers by command tag, and copy them into our
@@ -915,7 +913,7 @@ EventTriggerCommonSetup(Node *parsetree,
{
EventTriggerCacheItem *item = lfirst(lc);
- if (filter_event_trigger(&tag, item))
+ if (filter_event_trigger(tag, item))
{
/* We must plan to fire this trigger. */
runlist = lappend_oid(runlist, item->fnoid);
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 1b63048a77..a960d7ca80 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -51,7 +51,7 @@ static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD;
static void BuildEventTriggerCache(void);
static void InvalidateEventCacheCallback(Datum arg,
int cacheid, uint32 hashvalue);
-static int DecodeTextArrayToCString(Datum array, char ***cstringp);
+static void DecodeTextArrayToBitmapset(Datum array, Bitmapset **bms);
/*
* Search the event cache by trigger event.
@@ -180,10 +180,7 @@ BuildEventTriggerCache(void)
evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags,
RelationGetDescr(rel), &evttags_isnull);
if (!evttags_isnull)
- {
- item->ntags = DecodeTextArrayToCString(evttags, &item->tag);
- qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp);
- }
+ DecodeTextArrayToBitmapset(evttags, &item->tagset);
/* Add to cache entry. */
entry = hash_search(cache, &event, HASH_ENTER, &found);
@@ -215,18 +212,18 @@ BuildEventTriggerCache(void)
}
/*
- * Decode text[] to an array of C strings.
+ * Decode text[] to a Bitmapset of CommandTags.
*
* We could avoid a bit of overhead here if we were willing to duplicate some
* of the logic from deconstruct_array, but it doesn't seem worth the code
* complexity.
*/
-static int
-DecodeTextArrayToCString(Datum array, char ***cstringp)
+static void
+DecodeTextArrayToBitmapset(Datum array, Bitmapset **tagset)
{
ArrayType *arr = DatumGetArrayTypeP(array);
Datum *elems;
- char **cstring;
+ Bitmapset *bms;
int i;
int nelems;
@@ -234,13 +231,15 @@ DecodeTextArrayToCString(Datum array, char ***cstringp)
elog(ERROR, "expected 1-D text array");
deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems);
- cstring = palloc(nelems * sizeof(char *));
- for (i = 0; i < nelems; ++i)
- cstring[i] = TextDatumGetCString(elems[i]);
+ for (bms = NULL, i = 0; i < nelems; ++i)
+ {
+ char *str = TextDatumGetCString(elems[i]);
+ bms = bms_add_member(bms, GetCommandTagEnum(str));
+ pfree(str);
+ }
pfree(elems);
- *cstringp = cstring;
- return nelems;
+ *tagset = bms;
}
/*
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index faa2958b89..5123c4b850 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -25,7 +25,7 @@ typedef struct EventTriggerData
NodeTag type;
const char *event; /* event name */
Node *parsetree; /* parse tree */
- const char *tag; /* command tag */
+ CommandTag tag;
} EventTriggerData;
#define AT_REWRITE_ALTER_PERSISTENCE 0x01
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index 6c3ff81ba3..bc8ce48061 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -28,8 +28,7 @@ typedef struct
{
Oid fnoid; /* function to be called */
char enabled; /* as SESSION_REPLICATION_ROLE_* */
- int ntags; /* number of command tags */
- char **tag; /* command tags in SORTED order */
+ Bitmapset *tagset; /* command tags, or NULL if empty */
} EventTriggerCacheItem;
extern List *EventCacheLookup(EventTriggerEvent event);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 8a0f31b4b8..8ac2b67417 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -1473,7 +1473,7 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
case PLPGSQL_PROMISE_TG_TAG:
if (estate->evtrigdata == NULL)
elog(ERROR, "event trigger promise is not in an event trigger function");
- assign_text_var(estate, var, estate->evtrigdata->tag);
+ assign_text_var(estate, var, GetCommandTagName(estate->evtrigdata->tag));
break;
default:
--
2.21.1 (Apple Git-122.3)
v2-0003-Extending-the-event_trigger-regression-test.patchapplication/octet-stream; name=v2-0003-Extending-the-event_trigger-regression-test.patch; x-unix-mode=0644Download
From fc7b7b849288b10605adc5f2bb3618b557ef557f Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Sun, 2 Feb 2020 15:42:50 -0800
Subject: [PATCH v2 3/3] Extending the event_trigger regression test.
This commit adds coverage for all command tags being used
as tags within CREATE EVENT TRIGGER. The previous coverage
was incomplete.
---
src/test/regress/expected/event_trigger.out | 765 +++++++++++++++++++
src/test/regress/sql/event_trigger.sql | 784 ++++++++++++++++++++
2 files changed, 1549 insertions(+)
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 788be86b33..9e6c845540 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -9,6 +9,12 @@ BEGIN
RAISE NOTICE 'test_event_trigger: % %', tg_event, tg_tag;
END
$$ language plpgsql;
+-- OK
+create function test_event_trigger2() returns event_trigger as $$
+BEGIN
+ RAISE NOTICE 'test_event_trigger2: % %', tg_event, tg_tag;
+END
+$$ LANGUAGE plpgsql;
-- should fail, event triggers cannot have declared arguments
create function test_event_trigger_arg(name text)
returns event_trigger as $$ BEGIN RETURN 1; END $$ language plpgsql;
@@ -80,6 +86,765 @@ create event trigger regress_event_trigger2 on ddl_command_start
execute procedure test_event_trigger();
-- OK
comment on event trigger regress_event_trigger is 'test comment';
+-- These are all unsupported
+create event trigger regress_event_triger_NULL on ddl_command_start
+ when tag in ('')
+ execute procedure test_event_trigger2();
+ERROR: filter value "" not recognized for filter variable "tag"
+create event trigger regress_event_triger_UNKNOWN on ddl_command_start
+ when tag in ('???')
+ execute procedure test_event_trigger2();
+ERROR: filter value "???" not recognized for filter variable "tag"
+create event trigger regress_event_trigger_ALTER_DATABASE on ddl_command_start
+ when tag in ('ALTER DATABASE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ALTER DATABASE
+create event trigger regress_event_trigger_ALTER_EVENT_TRIGGER on ddl_command_start
+ when tag in ('ALTER EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ALTER EVENT TRIGGER
+create event trigger regress_event_trigger_ALTER_ROLE on ddl_command_start
+ when tag in ('ALTER ROLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ALTER ROLE
+create event trigger regress_event_trigger_ALTER_SYSTEM on ddl_command_start
+ when tag in ('ALTER SYSTEM')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ALTER SYSTEM
+create event trigger regress_event_trigger_ALTER_TABLESPACE on ddl_command_start
+ when tag in ('ALTER TABLESPACE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ALTER TABLESPACE
+create event trigger regress_event_trigger_ANALYZE on ddl_command_start
+ when tag in ('ANALYZE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ANALYZE
+create event trigger regress_event_trigger_BEGIN on ddl_command_start
+ when tag in ('BEGIN')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for BEGIN
+create event trigger regress_event_trigger_CALL on ddl_command_start
+ when tag in ('CALL')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CALL
+create event trigger regress_event_trigger_CHECKPOINT on ddl_command_start
+ when tag in ('CHECKPOINT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CHECKPOINT
+create event trigger regress_event_trigger_CLOSE on ddl_command_start
+ when tag in ('CLOSE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CLOSE
+create event trigger regress_event_trigger_CLOSE_CURSOR on ddl_command_start
+ when tag in ('CLOSE CURSOR')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CLOSE CURSOR
+create event trigger regress_event_trigger_CLOSE_CURSOR_ALL on ddl_command_start
+ when tag in ('CLOSE CURSOR ALL')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CLOSE CURSOR ALL
+create event trigger regress_event_trigger_CLUSTER on ddl_command_start
+ when tag in ('CLUSTER')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CLUSTER
+create event trigger regress_event_trigger_COMMIT on ddl_command_start
+ when tag in ('COMMIT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for COMMIT
+create event trigger regress_event_trigger_COMMIT_PREPARED on ddl_command_start
+ when tag in ('COMMIT PREPARED')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for COMMIT PREPARED
+create event trigger regress_event_trigger_COPY on ddl_command_start
+ when tag in ('COPY')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for COPY
+create event trigger regress_event_trigger_COPY_FROM on ddl_command_start
+ when tag in ('COPY FROM')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for COPY FROM
+create event trigger regress_event_trigger_CREATE_DATABASE on ddl_command_start
+ when tag in ('CREATE DATABASE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CREATE DATABASE
+create event trigger regress_event_trigger_CREATE_EVENT_TRIGGER on ddl_command_start
+ when tag in ('CREATE EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CREATE EVENT TRIGGER
+create event trigger regress_event_trigger_CREATE_ROLE on ddl_command_start
+ when tag in ('CREATE ROLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CREATE ROLE
+create event trigger regress_event_trigger_CREATE_TABLESPACE on ddl_command_start
+ when tag in ('CREATE TABLESPACE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for CREATE TABLESPACE
+create event trigger regress_event_trigger_DEALLOCATE on ddl_command_start
+ when tag in ('DEALLOCATE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DEALLOCATE
+create event trigger regress_event_trigger_DEALLOCATE_ALL on ddl_command_start
+ when tag in ('DEALLOCATE ALL')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DEALLOCATE ALL
+create event trigger regress_event_trigger_DECLARE_CURSOR on ddl_command_start
+ when tag in ('DECLARE CURSOR')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DECLARE CURSOR
+create event trigger regress_event_trigger_DELETE on ddl_command_start
+ when tag in ('DELETE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DELETE
+create event trigger regress_event_trigger_DISCARD on ddl_command_start
+ when tag in ('DISCARD')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DISCARD
+create event trigger regress_event_trigger_DISCARD_ALL on ddl_command_start
+ when tag in ('DISCARD ALL')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DISCARD ALL
+create event trigger regress_event_trigger_DISCARD_PLANS on ddl_command_start
+ when tag in ('DISCARD PLANS')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DISCARD PLANS
+create event trigger regress_event_trigger_DISCARD_SEQUENCES on ddl_command_start
+ when tag in ('DISCARD SEQUENCES')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DISCARD SEQUENCES
+create event trigger regress_event_trigger_DISCARD_TEMP on ddl_command_start
+ when tag in ('DISCARD TEMP')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DISCARD TEMP
+create event trigger regress_event_trigger_DO on ddl_command_start
+ when tag in ('DO')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DO
+create event trigger regress_event_trigger_DROP_DATABASE on ddl_command_start
+ when tag in ('DROP DATABASE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DROP DATABASE
+create event trigger regress_event_trigger_DROP_EVENT_TRIGGER on ddl_command_start
+ when tag in ('DROP EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DROP EVENT TRIGGER
+create event trigger regress_event_trigger_DROP_REPLICATION_SLOT on ddl_command_start
+ when tag in ('DROP REPLICATION SLOT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DROP REPLICATION SLOT
+create event trigger regress_event_trigger_DROP_ROLE on ddl_command_start
+ when tag in ('DROP ROLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DROP ROLE
+create event trigger regress_event_trigger_DROP_TABLESPACE on ddl_command_start
+ when tag in ('DROP TABLESPACE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for DROP TABLESPACE
+create event trigger regress_event_trigger_EXECUTE on ddl_command_start
+ when tag in ('EXECUTE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for EXECUTE
+create event trigger regress_event_trigger_EXPLAIN on ddl_command_start
+ when tag in ('EXPLAIN')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for EXPLAIN
+create event trigger regress_event_trigger_FETCH on ddl_command_start
+ when tag in ('FETCH')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for FETCH
+create event trigger regress_event_trigger_GRANT_ROLE on ddl_command_start
+ when tag in ('GRANT ROLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for GRANT ROLE
+create event trigger regress_event_trigger_INSERT on ddl_command_start
+ when tag in ('INSERT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for INSERT
+create event trigger regress_event_trigger_LISTEN on ddl_command_start
+ when tag in ('LISTEN')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for LISTEN
+create event trigger regress_event_trigger_LOAD on ddl_command_start
+ when tag in ('LOAD')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for LOAD
+create event trigger regress_event_trigger_LOCK_TABLE on ddl_command_start
+ when tag in ('LOCK TABLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for LOCK TABLE
+create event trigger regress_event_trigger_MOVE on ddl_command_start
+ when tag in ('MOVE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for MOVE
+create event trigger regress_event_trigger_NOTIFY on ddl_command_start
+ when tag in ('NOTIFY')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for NOTIFY
+create event trigger regress_event_trigger_PREPARE on ddl_command_start
+ when tag in ('PREPARE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for PREPARE
+create event trigger regress_event_trigger_PREPARE_TRANSACTION on ddl_command_start
+ when tag in ('PREPARE TRANSACTION')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for PREPARE TRANSACTION
+create event trigger regress_event_trigger_REASSIGN_OWNED on ddl_command_start
+ when tag in ('REASSIGN OWNED')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for REASSIGN OWNED
+create event trigger regress_event_trigger_REINDEX on ddl_command_start
+ when tag in ('REINDEX')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for REINDEX
+create event trigger regress_event_trigger_RELEASE on ddl_command_start
+ when tag in ('RELEASE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for RELEASE
+create event trigger regress_event_trigger_RESET on ddl_command_start
+ when tag in ('RESET')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for RESET
+create event trigger regress_event_trigger_REVOKE on ddl_command_start
+ when tag in ('REVOKE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_REVOKE_ROLE on ddl_command_start
+ when tag in ('REVOKE ROLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for REVOKE ROLE
+create event trigger regress_event_trigger_ROLLBACK on ddl_command_start
+ when tag in ('ROLLBACK')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ROLLBACK
+create event trigger regress_event_trigger_ROLLBACK_PREPARED on ddl_command_start
+ when tag in ('ROLLBACK PREPARED')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for ROLLBACK PREPARED
+create event trigger regress_event_trigger_SAVEPOINT on ddl_command_start
+ when tag in ('SAVEPOINT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SAVEPOINT
+create event trigger regress_event_trigger_SECURITY_LABEL on ddl_command_start
+ when tag in ('SECURITY LABEL')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_SELECT on ddl_command_start
+ when tag in ('SELECT')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SELECT
+create event trigger regress_event_trigger_SELECT_FOR_KEY_SHARE on ddl_command_start
+ when tag in ('SELECT FOR KEY SHARE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SELECT FOR KEY SHARE
+create event trigger regress_event_trigger_SELECT_FOR_NO_KEY_UPDATE on ddl_command_start
+ when tag in ('SELECT FOR NO KEY UPDATE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SELECT FOR NO KEY UPDATE
+create event trigger regress_event_trigger_SELECT_FOR_SHARE on ddl_command_start
+ when tag in ('SELECT FOR SHARE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SELECT FOR SHARE
+create event trigger regress_event_trigger_SELECT_FOR_UPDATE on ddl_command_start
+ when tag in ('SELECT FOR UPDATE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SELECT FOR UPDATE
+create event trigger regress_event_trigger_SET on ddl_command_start
+ when tag in ('SET')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SET
+create event trigger regress_event_trigger_SET_CONSTRAINTS on ddl_command_start
+ when tag in ('SET CONSTRAINTS')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SET CONSTRAINTS
+create event trigger regress_event_trigger_SHOW on ddl_command_start
+ when tag in ('SHOW')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for SHOW
+create event trigger regress_event_trigger_START_TRANSACTION on ddl_command_start
+ when tag in ('START TRANSACTION')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for START TRANSACTION
+create event trigger regress_event_trigger_TRUNCATE_TABLE on ddl_command_start
+ when tag in ('TRUNCATE TABLE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for TRUNCATE TABLE
+create event trigger regress_event_trigger_UNLISTEN on ddl_command_start
+ when tag in ('UNLISTEN')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for UNLISTEN
+create event trigger regress_event_trigger_UPDATE on ddl_command_start
+ when tag in ('UPDATE')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for UPDATE
+create event trigger regress_event_trigger_VACUUM on ddl_command_start
+ when tag in ('VACUUM')
+ execute procedure test_event_trigger2();
+ERROR: event triggers are not supported for VACUUM
+-- These are all ok
+create event trigger regress_event_trigger_ALTER_ACCESS_METHOD on ddl_command_start
+ when tag in ('ALTER ACCESS METHOD')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_AGGREGATE on ddl_command_start
+ when tag in ('ALTER AGGREGATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_CAST on ddl_command_start
+ when tag in ('ALTER CAST')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_COLLATION on ddl_command_start
+ when tag in ('ALTER COLLATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_CONSTRAINT on ddl_command_start
+ when tag in ('ALTER CONSTRAINT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_CONVERSION on ddl_command_start
+ when tag in ('ALTER CONVERSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_DEFAULT_PRIVILEGES on ddl_command_start
+ when tag in ('ALTER DEFAULT PRIVILEGES')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_DOMAIN on ddl_command_start
+ when tag in ('ALTER DOMAIN')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_EXTENSION on ddl_command_start
+ when tag in ('ALTER EXTENSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('ALTER FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_FOREIGN_TABLE on ddl_command_start
+ when tag in ('ALTER FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_FUNCTION on ddl_command_start
+ when tag in ('ALTER FUNCTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_INDEX on ddl_command_start
+ when tag in ('ALTER INDEX')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_LANGUAGE on ddl_command_start
+ when tag in ('ALTER LANGUAGE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_LARGE_OBJECT on ddl_command_start
+ when tag in ('ALTER LARGE OBJECT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('ALTER MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_OPERATOR on ddl_command_start
+ when tag in ('ALTER OPERATOR')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_OPERATOR_CLASS on ddl_command_start
+ when tag in ('ALTER OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('ALTER OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_POLICY on ddl_command_start
+ when tag in ('ALTER POLICY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_PROCEDURE on ddl_command_start
+ when tag in ('ALTER PROCEDURE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_PUBLICATION on ddl_command_start
+ when tag in ('ALTER PUBLICATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_ROUTINE on ddl_command_start
+ when tag in ('ALTER ROUTINE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_RULE on ddl_command_start
+ when tag in ('ALTER RULE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_SCHEMA on ddl_command_start
+ when tag in ('ALTER SCHEMA')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_SEQUENCE on ddl_command_start
+ when tag in ('ALTER SEQUENCE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_SERVER on ddl_command_start
+ when tag in ('ALTER SERVER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_STATISTICS on ddl_command_start
+ when tag in ('ALTER STATISTICS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_SUBSCRIPTION on ddl_command_start
+ when tag in ('ALTER SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TABLE on ddl_command_start
+ when tag in ('ALTER TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TRANSFORM on ddl_command_start
+ when tag in ('ALTER TRANSFORM')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TRIGGER on ddl_command_start
+ when tag in ('ALTER TRIGGER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_TYPE on ddl_command_start
+ when tag in ('ALTER TYPE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_USER_MAPPING on ddl_command_start
+ when tag in ('ALTER USER MAPPING')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_ALTER_VIEW on ddl_command_start
+ when tag in ('ALTER VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_COMMENT on ddl_command_start
+ when tag in ('COMMENT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_ACCESS_METHOD on ddl_command_start
+ when tag in ('CREATE ACCESS METHOD')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_AGGREGATE on ddl_command_start
+ when tag in ('CREATE AGGREGATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_CAST on ddl_command_start
+ when tag in ('CREATE CAST')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_COLLATION on ddl_command_start
+ when tag in ('CREATE COLLATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_CONSTRAINT on ddl_command_start
+ when tag in ('CREATE CONSTRAINT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_CONVERSION on ddl_command_start
+ when tag in ('CREATE CONVERSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_DOMAIN on ddl_command_start
+ when tag in ('CREATE DOMAIN')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_EXTENSION on ddl_command_start
+ when tag in ('CREATE EXTENSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('CREATE FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_FOREIGN_TABLE on ddl_command_start
+ when tag in ('CREATE FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_FUNCTION on ddl_command_start
+ when tag in ('CREATE FUNCTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_INDEX on ddl_command_start
+ when tag in ('CREATE INDEX')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_LANGUAGE on ddl_command_start
+ when tag in ('CREATE LANGUAGE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('CREATE MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_OPERATOR on ddl_command_start
+ when tag in ('CREATE OPERATOR')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_OPERATOR_CLASS on ddl_command_start
+ when tag in ('CREATE OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('CREATE OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_POLICY on ddl_command_start
+ when tag in ('CREATE POLICY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_PROCEDURE on ddl_command_start
+ when tag in ('CREATE PROCEDURE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_PUBLICATION on ddl_command_start
+ when tag in ('CREATE PUBLICATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_ROUTINE on ddl_command_start
+ when tag in ('CREATE ROUTINE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_RULE on ddl_command_start
+ when tag in ('CREATE RULE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_SCHEMA on ddl_command_start
+ when tag in ('CREATE SCHEMA')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_SEQUENCE on ddl_command_start
+ when tag in ('CREATE SEQUENCE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_SERVER on ddl_command_start
+ when tag in ('CREATE SERVER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_STATISTICS on ddl_command_start
+ when tag in ('CREATE STATISTICS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_SUBSCRIPTION on ddl_command_start
+ when tag in ('CREATE SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TABLE on ddl_command_start
+ when tag in ('CREATE TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TABLE_AS on ddl_command_start
+ when tag in ('CREATE TABLE AS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TRANSFORM on ddl_command_start
+ when tag in ('CREATE TRANSFORM')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TRIGGER on ddl_command_start
+ when tag in ('CREATE TRIGGER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_TYPE on ddl_command_start
+ when tag in ('CREATE TYPE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_USER_MAPPING on ddl_command_start
+ when tag in ('CREATE USER MAPPING')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_CREATE_VIEW on ddl_command_start
+ when tag in ('CREATE VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_ACCESS_METHOD on ddl_command_start
+ when tag in ('DROP ACCESS METHOD')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_AGGREGATE on ddl_command_start
+ when tag in ('DROP AGGREGATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_CAST on ddl_command_start
+ when tag in ('DROP CAST')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_COLLATION on ddl_command_start
+ when tag in ('DROP COLLATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_CONSTRAINT on ddl_command_start
+ when tag in ('DROP CONSTRAINT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_CONVERSION on ddl_command_start
+ when tag in ('DROP CONVERSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_DOMAIN on ddl_command_start
+ when tag in ('DROP DOMAIN')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_EXTENSION on ddl_command_start
+ when tag in ('DROP EXTENSION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('DROP FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_FOREIGN_TABLE on ddl_command_start
+ when tag in ('DROP FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_FUNCTION on ddl_command_start
+ when tag in ('DROP FUNCTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_INDEX on ddl_command_start
+ when tag in ('DROP INDEX')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_LANGUAGE on ddl_command_start
+ when tag in ('DROP LANGUAGE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('DROP MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_OPERATOR on ddl_command_start
+ when tag in ('DROP OPERATOR')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_OPERATOR_CLASS on ddl_command_start
+ when tag in ('DROP OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('DROP OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_OWNED on ddl_command_start
+ when tag in ('DROP OWNED')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_POLICY on ddl_command_start
+ when tag in ('DROP POLICY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_PROCEDURE on ddl_command_start
+ when tag in ('DROP PROCEDURE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_PUBLICATION on ddl_command_start
+ when tag in ('DROP PUBLICATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_ROUTINE on ddl_command_start
+ when tag in ('DROP ROUTINE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_RULE on ddl_command_start
+ when tag in ('DROP RULE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_SCHEMA on ddl_command_start
+ when tag in ('DROP SCHEMA')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_SEQUENCE on ddl_command_start
+ when tag in ('DROP SEQUENCE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_SERVER on ddl_command_start
+ when tag in ('DROP SERVER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_STATISTICS on ddl_command_start
+ when tag in ('DROP STATISTICS')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_SUBSCRIPTION on ddl_command_start
+ when tag in ('DROP SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TABLE on ddl_command_start
+ when tag in ('DROP TABLE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('DROP TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('DROP TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('DROP TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('DROP TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TRANSFORM on ddl_command_start
+ when tag in ('DROP TRANSFORM')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TRIGGER on ddl_command_start
+ when tag in ('DROP TRIGGER')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_TYPE on ddl_command_start
+ when tag in ('DROP TYPE')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_USER_MAPPING on ddl_command_start
+ when tag in ('DROP USER MAPPING')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_DROP_VIEW on ddl_command_start
+ when tag in ('DROP VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_GRANT on ddl_command_start
+ when tag in ('GRANT')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_IMPORT_FOREIGN_SCHEMA on ddl_command_start
+ when tag in ('IMPORT FOREIGN SCHEMA')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_REFRESH_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('REFRESH MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+create event trigger regress_event_trigger_SELECT_INTO on ddl_command_start
+ when tag in ('SELECT INTO')
+ execute procedure test_event_trigger2();
+-- drop should cascade to the event trigger
+drop function test_event_trigger2() cascade;
+NOTICE: test_event_trigger: ddl_command_start DROP FUNCTION
+NOTICE: test_event_trigger2: ddl_command_start DROP FUNCTION
+NOTICE: drop cascades to 122 other objects
+DETAIL: drop cascades to event trigger regress_event_trigger_revoke
+drop cascades to event trigger regress_event_trigger_security_label
+drop cascades to event trigger regress_event_trigger_alter_access_method
+drop cascades to event trigger regress_event_trigger_alter_aggregate
+drop cascades to event trigger regress_event_trigger_alter_cast
+drop cascades to event trigger regress_event_trigger_alter_collation
+drop cascades to event trigger regress_event_trigger_alter_constraint
+drop cascades to event trigger regress_event_trigger_alter_conversion
+drop cascades to event trigger regress_event_trigger_alter_default_privileges
+drop cascades to event trigger regress_event_trigger_alter_domain
+drop cascades to event trigger regress_event_trigger_alter_extension
+drop cascades to event trigger regress_event_trigger_alter_foreign_data_wrapper
+drop cascades to event trigger regress_event_trigger_alter_foreign_table
+drop cascades to event trigger regress_event_trigger_alter_function
+drop cascades to event trigger regress_event_trigger_alter_index
+drop cascades to event trigger regress_event_trigger_alter_language
+drop cascades to event trigger regress_event_trigger_alter_large_object
+drop cascades to event trigger regress_event_trigger_alter_materialized_view
+drop cascades to event trigger regress_event_trigger_alter_operator
+drop cascades to event trigger regress_event_trigger_alter_operator_class
+drop cascades to event trigger regress_event_trigger_alter_operator_family
+drop cascades to event trigger regress_event_trigger_alter_policy
+drop cascades to event trigger regress_event_trigger_alter_procedure
+drop cascades to event trigger regress_event_trigger_alter_publication
+drop cascades to event trigger regress_event_trigger_alter_routine
+drop cascades to event trigger regress_event_trigger_alter_rule
+drop cascades to event trigger regress_event_trigger_alter_schema
+drop cascades to event trigger regress_event_trigger_alter_sequence
+drop cascades to event trigger regress_event_trigger_alter_server
+drop cascades to event trigger regress_event_trigger_alter_statistics
+drop cascades to event trigger regress_event_trigger_alter_subscription
+drop cascades to event trigger regress_event_trigger_alter_table
+drop cascades to event trigger regress_event_trigger_alter_text_search_configuration
+drop cascades to event trigger regress_event_trigger_alter_text_search_dictionary
+drop cascades to event trigger regress_event_trigger_alter_text_search_parser
+drop cascades to event trigger regress_event_trigger_alter_text_search_template
+drop cascades to event trigger regress_event_trigger_alter_transform
+drop cascades to event trigger regress_event_trigger_alter_trigger
+drop cascades to event trigger regress_event_trigger_alter_type
+drop cascades to event trigger regress_event_trigger_alter_user_mapping
+drop cascades to event trigger regress_event_trigger_alter_view
+drop cascades to event trigger regress_event_trigger_comment
+drop cascades to event trigger regress_event_trigger_create_access_method
+drop cascades to event trigger regress_event_trigger_create_aggregate
+drop cascades to event trigger regress_event_trigger_create_cast
+drop cascades to event trigger regress_event_trigger_create_collation
+drop cascades to event trigger regress_event_trigger_create_constraint
+drop cascades to event trigger regress_event_trigger_create_conversion
+drop cascades to event trigger regress_event_trigger_create_domain
+drop cascades to event trigger regress_event_trigger_create_extension
+drop cascades to event trigger regress_event_trigger_create_foreign_data_wrapper
+drop cascades to event trigger regress_event_trigger_create_foreign_table
+drop cascades to event trigger regress_event_trigger_create_function
+drop cascades to event trigger regress_event_trigger_create_index
+drop cascades to event trigger regress_event_trigger_create_language
+drop cascades to event trigger regress_event_trigger_create_materialized_view
+drop cascades to event trigger regress_event_trigger_create_operator
+drop cascades to event trigger regress_event_trigger_create_operator_class
+drop cascades to event trigger regress_event_trigger_create_operator_family
+drop cascades to event trigger regress_event_trigger_create_policy
+drop cascades to event trigger regress_event_trigger_create_procedure
+drop cascades to event trigger regress_event_trigger_create_publication
+drop cascades to event trigger regress_event_trigger_create_routine
+drop cascades to event trigger regress_event_trigger_create_rule
+drop cascades to event trigger regress_event_trigger_create_schema
+drop cascades to event trigger regress_event_trigger_create_sequence
+drop cascades to event trigger regress_event_trigger_create_server
+drop cascades to event trigger regress_event_trigger_create_statistics
+drop cascades to event trigger regress_event_trigger_create_subscription
+drop cascades to event trigger regress_event_trigger_create_table
+drop cascades to event trigger regress_event_trigger_create_table_as
+drop cascades to event trigger regress_event_trigger_create_text_search_configuration
+drop cascades to event trigger regress_event_trigger_create_text_search_dictionary
+drop cascades to event trigger regress_event_trigger_create_text_search_parser
+drop cascades to event trigger regress_event_trigger_create_text_search_template
+drop cascades to event trigger regress_event_trigger_create_transform
+drop cascades to event trigger regress_event_trigger_create_trigger
+drop cascades to event trigger regress_event_trigger_create_type
+drop cascades to event trigger regress_event_trigger_create_user_mapping
+drop cascades to event trigger regress_event_trigger_create_view
+drop cascades to event trigger regress_event_trigger_drop_access_method
+drop cascades to event trigger regress_event_trigger_drop_aggregate
+drop cascades to event trigger regress_event_trigger_drop_cast
+drop cascades to event trigger regress_event_trigger_drop_collation
+drop cascades to event trigger regress_event_trigger_drop_constraint
+drop cascades to event trigger regress_event_trigger_drop_conversion
+drop cascades to event trigger regress_event_trigger_drop_domain
+drop cascades to event trigger regress_event_trigger_drop_extension
+drop cascades to event trigger regress_event_trigger_drop_foreign_data_wrapper
+drop cascades to event trigger regress_event_trigger_drop_foreign_table
+drop cascades to event trigger regress_event_trigger_drop_function
+drop cascades to event trigger regress_event_trigger_drop_index
+drop cascades to event trigger regress_event_trigger_drop_language
+drop cascades to event trigger regress_event_trigger_drop_materialized_view
+drop cascades to event trigger regress_event_trigger_drop_operator
+drop cascades to event trigger regress_event_trigger_drop_operator_class
+drop cascades to event trigger regress_event_trigger_drop_operator_family
+drop cascades to event trigger regress_event_trigger_drop_owned
+drop cascades to event trigger regress_event_trigger_drop_policy
+drop cascades to event trigger regress_event_trigger_drop_procedure
+and 22 other objects (see server log for list)
+NOTICE: test_event_trigger: ddl_command_end DROP FUNCTION
-- drop as non-superuser should fail
create role regress_evt_user;
set role regress_evt_user;
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 346168673d..cad02212ad 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -10,6 +10,13 @@ BEGIN
END
$$ language plpgsql;
+-- OK
+create function test_event_trigger2() returns event_trigger as $$
+BEGIN
+ RAISE NOTICE 'test_event_trigger2: % %', tg_event, tg_tag;
+END
+$$ LANGUAGE plpgsql;
+
-- should fail, event triggers cannot have declared arguments
create function test_event_trigger_arg(name text)
returns event_trigger as $$ BEGIN RETURN 1; END $$ language plpgsql;
@@ -82,6 +89,783 @@ create event trigger regress_event_trigger2 on ddl_command_start
-- OK
comment on event trigger regress_event_trigger is 'test comment';
+-- These are all unsupported
+create event trigger regress_event_triger_NULL on ddl_command_start
+ when tag in ('')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_triger_UNKNOWN on ddl_command_start
+ when tag in ('???')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_DATABASE on ddl_command_start
+ when tag in ('ALTER DATABASE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_EVENT_TRIGGER on ddl_command_start
+ when tag in ('ALTER EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_ROLE on ddl_command_start
+ when tag in ('ALTER ROLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_SYSTEM on ddl_command_start
+ when tag in ('ALTER SYSTEM')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TABLESPACE on ddl_command_start
+ when tag in ('ALTER TABLESPACE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ANALYZE on ddl_command_start
+ when tag in ('ANALYZE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_BEGIN on ddl_command_start
+ when tag in ('BEGIN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CALL on ddl_command_start
+ when tag in ('CALL')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CHECKPOINT on ddl_command_start
+ when tag in ('CHECKPOINT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CLOSE on ddl_command_start
+ when tag in ('CLOSE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CLOSE_CURSOR on ddl_command_start
+ when tag in ('CLOSE CURSOR')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CLOSE_CURSOR_ALL on ddl_command_start
+ when tag in ('CLOSE CURSOR ALL')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CLUSTER on ddl_command_start
+ when tag in ('CLUSTER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_COMMIT on ddl_command_start
+ when tag in ('COMMIT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_COMMIT_PREPARED on ddl_command_start
+ when tag in ('COMMIT PREPARED')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_COPY on ddl_command_start
+ when tag in ('COPY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_COPY_FROM on ddl_command_start
+ when tag in ('COPY FROM')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_DATABASE on ddl_command_start
+ when tag in ('CREATE DATABASE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_EVENT_TRIGGER on ddl_command_start
+ when tag in ('CREATE EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_ROLE on ddl_command_start
+ when tag in ('CREATE ROLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TABLESPACE on ddl_command_start
+ when tag in ('CREATE TABLESPACE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DEALLOCATE on ddl_command_start
+ when tag in ('DEALLOCATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DEALLOCATE_ALL on ddl_command_start
+ when tag in ('DEALLOCATE ALL')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DECLARE_CURSOR on ddl_command_start
+ when tag in ('DECLARE CURSOR')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DELETE on ddl_command_start
+ when tag in ('DELETE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DISCARD on ddl_command_start
+ when tag in ('DISCARD')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DISCARD_ALL on ddl_command_start
+ when tag in ('DISCARD ALL')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DISCARD_PLANS on ddl_command_start
+ when tag in ('DISCARD PLANS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DISCARD_SEQUENCES on ddl_command_start
+ when tag in ('DISCARD SEQUENCES')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DISCARD_TEMP on ddl_command_start
+ when tag in ('DISCARD TEMP')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DO on ddl_command_start
+ when tag in ('DO')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_DATABASE on ddl_command_start
+ when tag in ('DROP DATABASE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_EVENT_TRIGGER on ddl_command_start
+ when tag in ('DROP EVENT TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_REPLICATION_SLOT on ddl_command_start
+ when tag in ('DROP REPLICATION SLOT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_ROLE on ddl_command_start
+ when tag in ('DROP ROLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TABLESPACE on ddl_command_start
+ when tag in ('DROP TABLESPACE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_EXECUTE on ddl_command_start
+ when tag in ('EXECUTE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_EXPLAIN on ddl_command_start
+ when tag in ('EXPLAIN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_FETCH on ddl_command_start
+ when tag in ('FETCH')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_GRANT_ROLE on ddl_command_start
+ when tag in ('GRANT ROLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_INSERT on ddl_command_start
+ when tag in ('INSERT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_LISTEN on ddl_command_start
+ when tag in ('LISTEN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_LOAD on ddl_command_start
+ when tag in ('LOAD')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_LOCK_TABLE on ddl_command_start
+ when tag in ('LOCK TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_MOVE on ddl_command_start
+ when tag in ('MOVE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_NOTIFY on ddl_command_start
+ when tag in ('NOTIFY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_PREPARE on ddl_command_start
+ when tag in ('PREPARE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_PREPARE_TRANSACTION on ddl_command_start
+ when tag in ('PREPARE TRANSACTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_REASSIGN_OWNED on ddl_command_start
+ when tag in ('REASSIGN OWNED')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_REINDEX on ddl_command_start
+ when tag in ('REINDEX')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_RELEASE on ddl_command_start
+ when tag in ('RELEASE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_RESET on ddl_command_start
+ when tag in ('RESET')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_REVOKE on ddl_command_start
+ when tag in ('REVOKE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_REVOKE_ROLE on ddl_command_start
+ when tag in ('REVOKE ROLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ROLLBACK on ddl_command_start
+ when tag in ('ROLLBACK')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ROLLBACK_PREPARED on ddl_command_start
+ when tag in ('ROLLBACK PREPARED')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SAVEPOINT on ddl_command_start
+ when tag in ('SAVEPOINT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SECURITY_LABEL on ddl_command_start
+ when tag in ('SECURITY LABEL')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT on ddl_command_start
+ when tag in ('SELECT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT_FOR_KEY_SHARE on ddl_command_start
+ when tag in ('SELECT FOR KEY SHARE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT_FOR_NO_KEY_UPDATE on ddl_command_start
+ when tag in ('SELECT FOR NO KEY UPDATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT_FOR_SHARE on ddl_command_start
+ when tag in ('SELECT FOR SHARE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT_FOR_UPDATE on ddl_command_start
+ when tag in ('SELECT FOR UPDATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SET on ddl_command_start
+ when tag in ('SET')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SET_CONSTRAINTS on ddl_command_start
+ when tag in ('SET CONSTRAINTS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SHOW on ddl_command_start
+ when tag in ('SHOW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_START_TRANSACTION on ddl_command_start
+ when tag in ('START TRANSACTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_TRUNCATE_TABLE on ddl_command_start
+ when tag in ('TRUNCATE TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_UNLISTEN on ddl_command_start
+ when tag in ('UNLISTEN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_UPDATE on ddl_command_start
+ when tag in ('UPDATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_VACUUM on ddl_command_start
+ when tag in ('VACUUM')
+ execute procedure test_event_trigger2();
+
+-- These are all ok
+create event trigger regress_event_trigger_ALTER_ACCESS_METHOD on ddl_command_start
+ when tag in ('ALTER ACCESS METHOD')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_AGGREGATE on ddl_command_start
+ when tag in ('ALTER AGGREGATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_CAST on ddl_command_start
+ when tag in ('ALTER CAST')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_COLLATION on ddl_command_start
+ when tag in ('ALTER COLLATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_CONSTRAINT on ddl_command_start
+ when tag in ('ALTER CONSTRAINT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_CONVERSION on ddl_command_start
+ when tag in ('ALTER CONVERSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_DEFAULT_PRIVILEGES on ddl_command_start
+ when tag in ('ALTER DEFAULT PRIVILEGES')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_DOMAIN on ddl_command_start
+ when tag in ('ALTER DOMAIN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_EXTENSION on ddl_command_start
+ when tag in ('ALTER EXTENSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('ALTER FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_FOREIGN_TABLE on ddl_command_start
+ when tag in ('ALTER FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_FUNCTION on ddl_command_start
+ when tag in ('ALTER FUNCTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_INDEX on ddl_command_start
+ when tag in ('ALTER INDEX')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_LANGUAGE on ddl_command_start
+ when tag in ('ALTER LANGUAGE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_LARGE_OBJECT on ddl_command_start
+ when tag in ('ALTER LARGE OBJECT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('ALTER MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_OPERATOR on ddl_command_start
+ when tag in ('ALTER OPERATOR')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_OPERATOR_CLASS on ddl_command_start
+ when tag in ('ALTER OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('ALTER OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_POLICY on ddl_command_start
+ when tag in ('ALTER POLICY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_PROCEDURE on ddl_command_start
+ when tag in ('ALTER PROCEDURE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_PUBLICATION on ddl_command_start
+ when tag in ('ALTER PUBLICATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_ROUTINE on ddl_command_start
+ when tag in ('ALTER ROUTINE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_RULE on ddl_command_start
+ when tag in ('ALTER RULE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_SCHEMA on ddl_command_start
+ when tag in ('ALTER SCHEMA')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_SEQUENCE on ddl_command_start
+ when tag in ('ALTER SEQUENCE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_SERVER on ddl_command_start
+ when tag in ('ALTER SERVER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_STATISTICS on ddl_command_start
+ when tag in ('ALTER STATISTICS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_SUBSCRIPTION on ddl_command_start
+ when tag in ('ALTER SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TABLE on ddl_command_start
+ when tag in ('ALTER TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('ALTER TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TRANSFORM on ddl_command_start
+ when tag in ('ALTER TRANSFORM')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TRIGGER on ddl_command_start
+ when tag in ('ALTER TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_TYPE on ddl_command_start
+ when tag in ('ALTER TYPE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_USER_MAPPING on ddl_command_start
+ when tag in ('ALTER USER MAPPING')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_ALTER_VIEW on ddl_command_start
+ when tag in ('ALTER VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_COMMENT on ddl_command_start
+ when tag in ('COMMENT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_ACCESS_METHOD on ddl_command_start
+ when tag in ('CREATE ACCESS METHOD')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_AGGREGATE on ddl_command_start
+ when tag in ('CREATE AGGREGATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_CAST on ddl_command_start
+ when tag in ('CREATE CAST')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_COLLATION on ddl_command_start
+ when tag in ('CREATE COLLATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_CONSTRAINT on ddl_command_start
+ when tag in ('CREATE CONSTRAINT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_CONVERSION on ddl_command_start
+ when tag in ('CREATE CONVERSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_DOMAIN on ddl_command_start
+ when tag in ('CREATE DOMAIN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_EXTENSION on ddl_command_start
+ when tag in ('CREATE EXTENSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('CREATE FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_FOREIGN_TABLE on ddl_command_start
+ when tag in ('CREATE FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_FUNCTION on ddl_command_start
+ when tag in ('CREATE FUNCTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_INDEX on ddl_command_start
+ when tag in ('CREATE INDEX')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_LANGUAGE on ddl_command_start
+ when tag in ('CREATE LANGUAGE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('CREATE MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_OPERATOR on ddl_command_start
+ when tag in ('CREATE OPERATOR')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_OPERATOR_CLASS on ddl_command_start
+ when tag in ('CREATE OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('CREATE OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_POLICY on ddl_command_start
+ when tag in ('CREATE POLICY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_PROCEDURE on ddl_command_start
+ when tag in ('CREATE PROCEDURE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_PUBLICATION on ddl_command_start
+ when tag in ('CREATE PUBLICATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_ROUTINE on ddl_command_start
+ when tag in ('CREATE ROUTINE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_RULE on ddl_command_start
+ when tag in ('CREATE RULE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_SCHEMA on ddl_command_start
+ when tag in ('CREATE SCHEMA')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_SEQUENCE on ddl_command_start
+ when tag in ('CREATE SEQUENCE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_SERVER on ddl_command_start
+ when tag in ('CREATE SERVER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_STATISTICS on ddl_command_start
+ when tag in ('CREATE STATISTICS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_SUBSCRIPTION on ddl_command_start
+ when tag in ('CREATE SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TABLE on ddl_command_start
+ when tag in ('CREATE TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TABLE_AS on ddl_command_start
+ when tag in ('CREATE TABLE AS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('CREATE TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TRANSFORM on ddl_command_start
+ when tag in ('CREATE TRANSFORM')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TRIGGER on ddl_command_start
+ when tag in ('CREATE TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_TYPE on ddl_command_start
+ when tag in ('CREATE TYPE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_USER_MAPPING on ddl_command_start
+ when tag in ('CREATE USER MAPPING')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_CREATE_VIEW on ddl_command_start
+ when tag in ('CREATE VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_ACCESS_METHOD on ddl_command_start
+ when tag in ('DROP ACCESS METHOD')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_AGGREGATE on ddl_command_start
+ when tag in ('DROP AGGREGATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_CAST on ddl_command_start
+ when tag in ('DROP CAST')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_COLLATION on ddl_command_start
+ when tag in ('DROP COLLATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_CONSTRAINT on ddl_command_start
+ when tag in ('DROP CONSTRAINT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_CONVERSION on ddl_command_start
+ when tag in ('DROP CONVERSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_DOMAIN on ddl_command_start
+ when tag in ('DROP DOMAIN')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_EXTENSION on ddl_command_start
+ when tag in ('DROP EXTENSION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_FOREIGN_DATA_WRAPPER on ddl_command_start
+ when tag in ('DROP FOREIGN DATA WRAPPER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_FOREIGN_TABLE on ddl_command_start
+ when tag in ('DROP FOREIGN TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_FUNCTION on ddl_command_start
+ when tag in ('DROP FUNCTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_INDEX on ddl_command_start
+ when tag in ('DROP INDEX')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_LANGUAGE on ddl_command_start
+ when tag in ('DROP LANGUAGE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('DROP MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_OPERATOR on ddl_command_start
+ when tag in ('DROP OPERATOR')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_OPERATOR_CLASS on ddl_command_start
+ when tag in ('DROP OPERATOR CLASS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_OPERATOR_FAMILY on ddl_command_start
+ when tag in ('DROP OPERATOR FAMILY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_OWNED on ddl_command_start
+ when tag in ('DROP OWNED')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_POLICY on ddl_command_start
+ when tag in ('DROP POLICY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_PROCEDURE on ddl_command_start
+ when tag in ('DROP PROCEDURE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_PUBLICATION on ddl_command_start
+ when tag in ('DROP PUBLICATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_ROUTINE on ddl_command_start
+ when tag in ('DROP ROUTINE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_RULE on ddl_command_start
+ when tag in ('DROP RULE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_SCHEMA on ddl_command_start
+ when tag in ('DROP SCHEMA')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_SEQUENCE on ddl_command_start
+ when tag in ('DROP SEQUENCE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_SERVER on ddl_command_start
+ when tag in ('DROP SERVER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_STATISTICS on ddl_command_start
+ when tag in ('DROP STATISTICS')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_SUBSCRIPTION on ddl_command_start
+ when tag in ('DROP SUBSCRIPTION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TABLE on ddl_command_start
+ when tag in ('DROP TABLE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_CONFIGURATION on ddl_command_start
+ when tag in ('DROP TEXT SEARCH CONFIGURATION')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_DICTIONARY on ddl_command_start
+ when tag in ('DROP TEXT SEARCH DICTIONARY')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_PARSER on ddl_command_start
+ when tag in ('DROP TEXT SEARCH PARSER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TEXT_SEARCH_TEMPLATE on ddl_command_start
+ when tag in ('DROP TEXT SEARCH TEMPLATE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TRANSFORM on ddl_command_start
+ when tag in ('DROP TRANSFORM')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TRIGGER on ddl_command_start
+ when tag in ('DROP TRIGGER')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_TYPE on ddl_command_start
+ when tag in ('DROP TYPE')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_USER_MAPPING on ddl_command_start
+ when tag in ('DROP USER MAPPING')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_DROP_VIEW on ddl_command_start
+ when tag in ('DROP VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_GRANT on ddl_command_start
+ when tag in ('GRANT')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_IMPORT_FOREIGN_SCHEMA on ddl_command_start
+ when tag in ('IMPORT FOREIGN SCHEMA')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_REFRESH_MATERIALIZED_VIEW on ddl_command_start
+ when tag in ('REFRESH MATERIALIZED VIEW')
+ execute procedure test_event_trigger2();
+
+create event trigger regress_event_trigger_SELECT_INTO on ddl_command_start
+ when tag in ('SELECT INTO')
+ execute procedure test_event_trigger2();
+
+-- drop should cascade to the event trigger
+drop function test_event_trigger2() cascade;
+
-- drop as non-superuser should fail
create role regress_evt_user;
set role regress_evt_user;
--
2.21.1 (Apple Git-122.3)
Hi,
On 2020-02-04 18:18:52 -0800, Mark Dilger wrote:
In master, a number of functions pass a char *completionTag argument (really a char completionTag[COMPLETION_TAG_BUFSIZE]) which gets filled in with the string to return to the client from EndCommand. I have removed that kind of logic:
- /* save the rowcount if we're given a completionTag to fill */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);In the patch, this is replaced with a new struct, QueryCompletionData. That bit of code above is replaced with:
+ /* save the rowcount if we're given a qc to fill */ + if (qc) + SetQC(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed, DISPLAYFORMAT_NPROCESSED);For wire protocol compatibility, we have to track the display format.
When this gets to EndCommand, the display format allows the string to
be written exactly as the client will expect. If we ever get to the
point where we can break with that compatibility, the third member of
this struct, display_format, can be removed.
Hm. While I like not having this as strings a lot, I wish we could get
rid of this displayformat stuff.
These are replaced by switch() case statements over the possible commandTags:
+ switch (commandTag) + { + /* + * Supported idiosyncratic special cases. + */ + case COMMANDTAG_ALTER_DEFAULT_PRIVILEGES: + case COMMANDTAG_ALTER_LARGE_OBJECT: + case COMMANDTAG_COMMENT: + case COMMANDTAG_CREATE_TABLE_AS: + case COMMANDTAG_DROP_OWNED: + case COMMANDTAG_GRANT: + case COMMANDTAG_IMPORT_FOREIGN_SCHEMA: + case COMMANDTAG_REFRESH_MATERIALIZED_VIEW: + case COMMANDTAG_REVOKE: + case COMMANDTAG_SECURITY_LABEL: + case COMMANDTAG_SELECT_INTO:
The number of these makes me wonder if we should just have a metadata
table in one place, instead of needing to edit multiple
locations. Something like
const ... CommandTagBehaviour[] = {
[COMMANDTAG_INSERT] = {
.display_processed = true, .display_oid = true, ...},
[COMMANDTAG_CREATE_TABLE_AS] = {
.event_trigger = true, ...},
with the zero initialized defaults being the common cases.
Not sure if it's worth going there. But it's maybe worth thinking about
for a minute?
Averages for test set 1 by scale:
set scale tps avg_latency 90%< max_latency
1 1 3741 1.734 3.162 133.718
1 9 6124 0.904 1.05 230.547
1 81 5921 0.931 1.015 67.023Averages for test set 1 by clients:
set clients tps avg_latency 90%< max_latency
1 1 2163 0.461 0.514 24.414
1 4 5968 0.675 0.791 40.354
1 16 7655 2.433 3.922 366.519For command tag patch (branched from 1fd687a035):
postgresql % find src -type f -name "*.c" -or -name "*.h" | xargs cat | wc
1482969 5691908 45281399postgresql % find src -type f -name "*.o" | xargs cat | wc
38209 476243 18999752Averages for test set 1 by scale:
set scale tps avg_latency 90%< max_latency
1 1 3877 1.645 3.066 24.973
1 9 6383 0.859 1.032 64.566
1 81 5945 0.925 1.023 162.9Averages for test set 1 by clients:
set clients tps avg_latency 90%< max_latency
1 1 2141 0.466 0.522 11.531
1 4 5967 0.673 0.783 136.882
1 16 8096 2.292 3.817 104.026
Not bad.
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 9aa2b61600..5322c14ce4 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -594,7 +594,7 @@ pg_notify(PG_FUNCTION_ARGS) payload = text_to_cstring(PG_GETARG_TEXT_PP(1));/* For NOTIFY as a statement, this is checked in ProcessUtility */ - PreventCommandDuringRecovery("NOTIFY"); + PreventCommandDuringRecovery(COMMANDTAG_NOTIFY);Async_Notify(channel, payload);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 40a8ec1abd..4828e75bd5 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -1063,7 +1063,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,/* check read-only transaction and parallel mode */ if (XactReadOnly && !rel->rd_islocaltemp) - PreventCommandIfReadOnly("COPY FROM"); + PreventCommandIfReadOnly(COMMANDTAG_COPY_FROM);cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);
I'm not sure this really ought to be part of this change - seems like a
somewhat independent change to me. With less obvious benefits.
static event_trigger_command_tag_check_result -check_ddl_tag(const char *tag) +check_ddl_tag(CommandTag commandTag) { - const char *obtypename; - const event_trigger_support_data *etsd; + switch (commandTag) + { + /* + * Supported idiosyncratic special cases. + */ + case COMMANDTAG_ALTER_DEFAULT_PRIVILEGES: + case COMMANDTAG_ALTER_LARGE_OBJECT: + case COMMANDTAG_COMMENT: + case COMMANDTAG_CREATE_TABLE_AS: + case COMMANDTAG_DROP_OWNED: + case COMMANDTAG_GRANT: + case COMMANDTAG_IMPORT_FOREIGN_SCHEMA: + case COMMANDTAG_REFRESH_MATERIALIZED_VIEW: + case COMMANDTAG_REVOKE: + case COMMANDTAG_SECURITY_LABEL: + case COMMANDTAG_SELECT_INTO:- /* - * Handle some idiosyncratic special cases. - */ - if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 || - pg_strcasecmp(tag, "SELECT INTO") == 0 || - pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 || - pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 || - pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 || - pg_strcasecmp(tag, "COMMENT") == 0 || - pg_strcasecmp(tag, "GRANT") == 0 || - pg_strcasecmp(tag, "REVOKE") == 0 || - pg_strcasecmp(tag, "DROP OWNED") == 0 || - pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 || - pg_strcasecmp(tag, "SECURITY LABEL") == 0) - return EVENT_TRIGGER_COMMAND_TAG_OK; + /* + * Supported CREATE commands + */ + case COMMANDTAG_CREATE_ACCESS_METHOD: + case COMMANDTAG_CREATE_AGGREGATE: + case COMMANDTAG_CREATE_CAST: + case COMMANDTAG_CREATE_COLLATION: + case COMMANDTAG_CREATE_CONSTRAINT: + case COMMANDTAG_CREATE_CONVERSION: + case COMMANDTAG_CREATE_DOMAIN: + case COMMANDTAG_CREATE_EXTENSION: + case COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER: + case COMMANDTAG_CREATE_FOREIGN_TABLE: + case COMMANDTAG_CREATE_FUNCTION: + case COMMANDTAG_CREATE_INDEX: + case COMMANDTAG_CREATE_LANGUAGE: + case COMMANDTAG_CREATE_MATERIALIZED_VIEW: + case COMMANDTAG_CREATE_OPERATOR: + case COMMANDTAG_CREATE_OPERATOR_CLASS: + case COMMANDTAG_CREATE_OPERATOR_FAMILY: + case COMMANDTAG_CREATE_POLICY: + case COMMANDTAG_CREATE_PROCEDURE: + case COMMANDTAG_CREATE_PUBLICATION: + case COMMANDTAG_CREATE_ROUTINE: + case COMMANDTAG_CREATE_RULE: + case COMMANDTAG_CREATE_SCHEMA: + case COMMANDTAG_CREATE_SEQUENCE: + case COMMANDTAG_CREATE_SERVER: + case COMMANDTAG_CREATE_STATISTICS: + case COMMANDTAG_CREATE_SUBSCRIPTION: + case COMMANDTAG_CREATE_TABLE: + case COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION: + case COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY: + case COMMANDTAG_CREATE_TEXT_SEARCH_PARSER: + case COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE: + case COMMANDTAG_CREATE_TRANSFORM: + case COMMANDTAG_CREATE_TRIGGER: + case COMMANDTAG_CREATE_TYPE: + case COMMANDTAG_CREATE_USER_MAPPING: + case COMMANDTAG_CREATE_VIEW:- /* - * Otherwise, command should be CREATE, ALTER, or DROP. - */ - if (pg_strncasecmp(tag, "CREATE ", 7) == 0) - obtypename = tag + 7; - else if (pg_strncasecmp(tag, "ALTER ", 6) == 0) - obtypename = tag + 6; - else if (pg_strncasecmp(tag, "DROP ", 5) == 0) - obtypename = tag + 5; - else - return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED; + /* + * Supported ALTER commands + */ + case COMMANDTAG_ALTER_ACCESS_METHOD: + case COMMANDTAG_ALTER_AGGREGATE: + case COMMANDTAG_ALTER_CAST: + case COMMANDTAG_ALTER_COLLATION: + case COMMANDTAG_ALTER_CONSTRAINT: + case COMMANDTAG_ALTER_CONVERSION: + case COMMANDTAG_ALTER_DOMAIN: + case COMMANDTAG_ALTER_EXTENSION: + case COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER: + case COMMANDTAG_ALTER_FOREIGN_TABLE: + case COMMANDTAG_ALTER_FUNCTION: + case COMMANDTAG_ALTER_INDEX: + case COMMANDTAG_ALTER_LANGUAGE: + case COMMANDTAG_ALTER_MATERIALIZED_VIEW: + case COMMANDTAG_ALTER_OPERATOR: + case COMMANDTAG_ALTER_OPERATOR_CLASS: + case COMMANDTAG_ALTER_OPERATOR_FAMILY: + case COMMANDTAG_ALTER_POLICY: + case COMMANDTAG_ALTER_PROCEDURE: + case COMMANDTAG_ALTER_PUBLICATION: + case COMMANDTAG_ALTER_ROUTINE: + case COMMANDTAG_ALTER_RULE: + case COMMANDTAG_ALTER_SCHEMA: + case COMMANDTAG_ALTER_SEQUENCE: + case COMMANDTAG_ALTER_SERVER: + case COMMANDTAG_ALTER_STATISTICS: + case COMMANDTAG_ALTER_SUBSCRIPTION: + case COMMANDTAG_ALTER_TABLE: + case COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION: + case COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY: + case COMMANDTAG_ALTER_TEXT_SEARCH_PARSER: + case COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE: + case COMMANDTAG_ALTER_TRANSFORM: + case COMMANDTAG_ALTER_TRIGGER: + case COMMANDTAG_ALTER_TYPE: + case COMMANDTAG_ALTER_USER_MAPPING: + case COMMANDTAG_ALTER_VIEW:- /* - * ...and the object type should be something recognizable. - */ - for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++) - if (pg_strcasecmp(etsd->obtypename, obtypename) == 0) + /* + * Supported DROP commands + */ + case COMMANDTAG_DROP_ACCESS_METHOD: + case COMMANDTAG_DROP_AGGREGATE: + case COMMANDTAG_DROP_CAST: + case COMMANDTAG_DROP_COLLATION: + case COMMANDTAG_DROP_CONSTRAINT: + case COMMANDTAG_DROP_CONVERSION: + case COMMANDTAG_DROP_DOMAIN: + case COMMANDTAG_DROP_EXTENSION: + case COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER: + case COMMANDTAG_DROP_FOREIGN_TABLE: + case COMMANDTAG_DROP_FUNCTION: + case COMMANDTAG_DROP_INDEX: + case COMMANDTAG_DROP_LANGUAGE: + case COMMANDTAG_DROP_MATERIALIZED_VIEW: + case COMMANDTAG_DROP_OPERATOR: + case COMMANDTAG_DROP_OPERATOR_CLASS: + case COMMANDTAG_DROP_OPERATOR_FAMILY: + case COMMANDTAG_DROP_POLICY: + case COMMANDTAG_DROP_PROCEDURE: + case COMMANDTAG_DROP_PUBLICATION: + case COMMANDTAG_DROP_ROUTINE: + case COMMANDTAG_DROP_RULE: + case COMMANDTAG_DROP_SCHEMA: + case COMMANDTAG_DROP_SEQUENCE: + case COMMANDTAG_DROP_SERVER: + case COMMANDTAG_DROP_STATISTICS: + case COMMANDTAG_DROP_SUBSCRIPTION: + case COMMANDTAG_DROP_TABLE: + case COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION: + case COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY: + case COMMANDTAG_DROP_TEXT_SEARCH_PARSER: + case COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE: + case COMMANDTAG_DROP_TRANSFORM: + case COMMANDTAG_DROP_TRIGGER: + case COMMANDTAG_DROP_TYPE: + case COMMANDTAG_DROP_USER_MAPPING: + case COMMANDTAG_DROP_VIEW: + return EVENT_TRIGGER_COMMAND_TAG_OK; + + /* + * Unsupported CREATE commands + */ + case COMMANDTAG_CREATE_DATABASE: + case COMMANDTAG_CREATE_EVENT_TRIGGER: + case COMMANDTAG_CREATE_ROLE: + case COMMANDTAG_CREATE_TABLESPACE: + + /* + * Unsupported ALTER commands + */ + case COMMANDTAG_ALTER_DATABASE: + case COMMANDTAG_ALTER_EVENT_TRIGGER: + case COMMANDTAG_ALTER_ROLE: + case COMMANDTAG_ALTER_TABLESPACE: + + /* + * Unsupported DROP commands + */ + case COMMANDTAG_DROP_DATABASE: + case COMMANDTAG_DROP_EVENT_TRIGGER: + case COMMANDTAG_DROP_ROLE: + case COMMANDTAG_DROP_TABLESPACE: + + /* + * Other unsupported commands. These used to return + * EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED prior to the + * conversion of commandTag from string to enum. + */ + case COMMANDTAG_ALTER_SYSTEM: + case COMMANDTAG_ANALYZE: + case COMMANDTAG_BEGIN: + case COMMANDTAG_CALL: + case COMMANDTAG_CHECKPOINT: + case COMMANDTAG_CLOSE: + case COMMANDTAG_CLOSE_CURSOR: + case COMMANDTAG_CLOSE_CURSOR_ALL: + case COMMANDTAG_CLUSTER: + case COMMANDTAG_COMMIT: + case COMMANDTAG_COMMIT_PREPARED: + case COMMANDTAG_COPY: + case COMMANDTAG_COPY_FROM: + case COMMANDTAG_DEALLOCATE: + case COMMANDTAG_DEALLOCATE_ALL: + case COMMANDTAG_DECLARE_CURSOR: + case COMMANDTAG_DELETE: + case COMMANDTAG_DISCARD: + case COMMANDTAG_DISCARD_ALL: + case COMMANDTAG_DISCARD_PLANS: + case COMMANDTAG_DISCARD_SEQUENCES: + case COMMANDTAG_DISCARD_TEMP: + case COMMANDTAG_DO: + case COMMANDTAG_DROP_REPLICATION_SLOT: + case COMMANDTAG_EXECUTE: + case COMMANDTAG_EXPLAIN: + case COMMANDTAG_FETCH: + case COMMANDTAG_GRANT_ROLE: + case COMMANDTAG_INSERT: + case COMMANDTAG_LISTEN: + case COMMANDTAG_LOAD: + case COMMANDTAG_LOCK_TABLE: + case COMMANDTAG_MOVE: + case COMMANDTAG_NOTIFY: + case COMMANDTAG_PREPARE: + case COMMANDTAG_PREPARE_TRANSACTION: + case COMMANDTAG_REASSIGN_OWNED: + case COMMANDTAG_REINDEX: + case COMMANDTAG_RELEASE: + case COMMANDTAG_RESET: + case COMMANDTAG_REVOKE_ROLE: + case COMMANDTAG_ROLLBACK: + case COMMANDTAG_ROLLBACK_PREPARED: + case COMMANDTAG_SAVEPOINT: + case COMMANDTAG_SELECT: + case COMMANDTAG_SELECT_FOR_KEY_SHARE: + case COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE: + case COMMANDTAG_SELECT_FOR_SHARE: + case COMMANDTAG_SELECT_FOR_UPDATE: + case COMMANDTAG_SET: + case COMMANDTAG_SET_CONSTRAINTS: + case COMMANDTAG_SHOW: + case COMMANDTAG_START_TRANSACTION: + case COMMANDTAG_TRUNCATE_TABLE: + case COMMANDTAG_UNLISTEN: + case COMMANDTAG_UPDATE: + case COMMANDTAG_VACUUM: + return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED; + case COMMANDTAG_UNKNOWN: break; - if (etsd->obtypename == NULL) - return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED; - if (!etsd->supported) - return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED; - return EVENT_TRIGGER_COMMAND_TAG_OK; + } + return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED; }
This is pretty painful.
@@ -745,7 +902,7 @@ EventTriggerCommonSetup(Node *parsetree,
return NIL;/* Get the command tag. */ - tag = CreateCommandTag(parsetree); + tag = GetCommandTagName(CreateCommandTag(parsetree));/* * Filter list of event triggers by command tag, and copy them into our @@ -2136,7 +2293,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS) /* objsubid */ values[i++] = Int32GetDatum(addr.objectSubId); /* command tag */ - values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree)); + values[i++] = CStringGetTextDatum(GetCommandTagName(CreateCommandTag(cmd->parsetree))); /* object_type */ values[i++] = CStringGetTextDatum(type); /* schema */ @@ -2161,7 +2318,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS) /* objsubid */ nulls[i++] = true; /* command tag */ - values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree)); + values[i++] = CStringGetTextDatum(GetCommandTagName(CreateCommandTag(cmd->parsetree))); /* object_type */ values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype)); /* schema */
So GetCommandTagName we commonly do twice for some reason? Once in
EventTriggerCommonSetup() and then again in
pg_event_trigger_ddl_commands()? Why is EventTriggerData.tag still the
string?
Assert(list_length(plan->plancache_list) == 1); @@ -1469,7 +1469,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /* translator: %s is a SQL statement name */ errmsg("%s is not allowed in a non-volatile function", - CreateCommandTag((Node *) pstmt)))); + GetCommandTagName(CreateCommandTag((Node *) pstmt)))));
Probably worth having a wrapper for this - these lines are pretty long,
and there quite a number of cases like it in the patch.
@@ -172,11 +175,38 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestRemoteSimple:/* - * We assume the commandTag is plain ASCII and therefore requires - * no encoding conversion. + * We assume the tagname is plain ASCII and therefore + * requires no encoding conversion. */ - pq_putmessage('C', commandTag, strlen(commandTag) + 1); - break; + tagname = GetCommandTagName(qc->commandTag); + switch (qc->display_format) + { + case DISPLAYFORMAT_PLAIN: + pq_putmessage('C', tagname, strlen(tagname) + 1); + break; + case DISPLAYFORMAT_LAST_OID: + /* + * We no longer display LastOid, but to preserve the wire protocol, + * we write InvalidOid where the LastOid used to be written. For + * efficiency in the snprintf(), hard-code InvalidOid as zero. + */ + Assert(InvalidOid == 0); + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, + "%s 0 " UINT64_FORMAT, + tagname, + qc->nprocessed); + pq_putmessage('C', completionTag, strlen(completionTag) + 1); + break; + case DISPLAYFORMAT_NPROCESSED: + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, + "%s " UINT64_FORMAT, + tagname, + qc->nprocessed); + pq_putmessage('C', completionTag, strlen(completionTag) + 1); + break; + default: + elog(ERROR, "Invalid display_format in EndCommand"); + }
Imo there should only be one pq_putmessage(). Also think this type of
default: is a bad idea, just prevents the compiler from warning if we
were to ever introduce a new variant of DISPLAYFORMAT_*, without
providing any meaningful additional security.
@@ -855,7 +889,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case T_DiscardStmt: /* should we allow DISCARD PLANS? */ - CheckRestrictedOperation("DISCARD"); + CheckRestrictedOperation(COMMANDTAG_DISCARD); DiscardCommand((DiscardStmt *) parsetree, isTopLevel); break;@@ -974,7 +1008,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecuteGrantStmt(stmt);
}
@@ -987,7 +1021,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecDropStmt(stmt, isTopLevel);
}
@@ -1000,7 +1034,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecRenameStmt(stmt);
}
@@ -1013,7 +1047,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectDependsStmt(stmt, NULL);
}
@@ -1026,7 +1060,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
}
@@ -1039,7 +1073,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterOwnerStmt(stmt);
}
@@ -1052,7 +1086,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
CommentObject(stmt);
break;
@@ -1065,7 +1099,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
Not this patch's fault or task. But I hate this type of code - needing
to touch a dozen places for new type of statement is just
insane. utility.c should long have been rewritten to just have one
metadata table for nearly all of this. Perhaps with a few callbacks for
special cases.
+static const char * tag_names[] = { + "???", + "ALTER ACCESS METHOD", + "ALTER AGGREGATE", + "ALTER CAST",
This seems problematic to maintain, because the order needs to match
between this and something defined in a header - and there's no
guarantee a misordering is immediately noticeable. We should either go
for my metadata table idea, or at least rewrite this, even if more
verbose, to something like
static const char * tag_names[] = {
[COMMAND_TAG_ALTER_ACCESS_METHOD] = "ALTER ACCESS METHOD",
...
I think the fact that this would show up in a grep for
COMMAND_TAG_ALTER_ACCESS_METHOD is good too.
+/* + * Search CommandTag by name + * + * Returns CommandTag, or COMMANDTAG_UNKNOWN if not recognized + */ +CommandTag +GetCommandTagEnum(const char *commandname) +{ + const char **base, **last, **position; + int result; + + OPTIONALLY_CHECK_COMMAND_TAGS(); + if (commandname == NULL || *commandname == '\0') + return COMMANDTAG_UNKNOWN; + + base = tag_names; + last = tag_names + tag_name_length - 1; + while (last >= base) + { + position = base + ((last - base) >> 1); + result = pg_strcasecmp(commandname, *position); + if (result == 0) + return (CommandTag) (position - tag_names); + else if (result < 0) + last = position - 1; + else + base = position + 1; + } + return COMMANDTAG_UNKNOWN; +}
This seems pretty grotty - but you get rid of it later. See my comments there.
+#ifdef COMMANDTAG_CHECKING +bool +CheckCommandTagEnum() +{ + CommandTag i, j; + + if (FIRST_COMMAND_TAG < 0 || LAST_COMMAND_TAG < 0 || LAST_COMMAND_TAG < FIRST_COMMAND_TAG) + { + elog(ERROR, "FIRST_COMMAND_TAG (%u), LAST_COMMAND_TAG (%u) not reasonable", + (unsigned int) FIRST_COMMAND_TAG, (unsigned int) LAST_COMMAND_TAG); + return false; + } + if (FIRST_COMMAND_TAG != (CommandTag)0) + { + elog(ERROR, "FIRST_COMMAND_TAG (%u) != 0", (unsigned int) FIRST_COMMAND_TAG); + return false; + } + if (LAST_COMMAND_TAG != (CommandTag)(tag_name_length - 1)) + { + elog(ERROR, "LAST_COMMAND_TAG (%u) != tag_name_length (%u)", + (unsigned int) LAST_COMMAND_TAG, (unsigned int) tag_name_length); + return false; + }
These all seem to want to be static asserts.
+ for (i = FIRST_COMMAND_TAG; i < LAST_COMMAND_TAG; i++) + { + for (j = i+1; j < LAST_COMMAND_TAG; j++) + { + int cmp = strcmp(tag_names[i], tag_names[j]); + if (cmp == 0) + { + elog(ERROR, "Found duplicate tag_name: \"%s\"", + tag_names[i]); + return false; + } + if (cmp > 0) + { + elog(ERROR, "Found commandnames out of order: \"%s\" before \"%s\"", + tag_names[i], tag_names[j]); + return false; + } + } + } + return true; +}
And I think we could get rid of this with my earlier suggestions?
+/* + * BEWARE: These are in sorted order, but ordered by their printed + * values in the tag_name list (see common/commandtag.c). + * In particular it matters because the sort ordering changes + * when you replace a space with an underscore. To wit: + * + * "CREATE TABLE" + * "CREATE TABLE AS" + * "CREATE TABLESPACE" + * + * but... + * + * CREATE_TABLE + * CREATE_TABLESPACE + * CREATE_TABLE_AS + * + * It also matters that COMMANDTAG_UNKNOWN is written "???". + * + * If you add a value here, add it in common/commandtag.c also, and + * be careful to get the ordering right. You can build with + * COMMANDTAG_CHECKING to have this automatically checked + * at runtime, but that adds considerable overhead, so do so sparingly. + */ +typedef enum CommandTag +{
This seems pretty darn nightmarish.
+#define FIRST_COMMAND_TAG COMMANDTAG_UNKNOWN + COMMANDTAG_UNKNOWN, + COMMANDTAG_ALTER_ACCESS_METHOD, + COMMANDTAG_ALTER_AGGREGATE, + COMMANDTAG_ALTER_CAST, + COMMANDTAG_ALTER_COLLATION, + COMMANDTAG_ALTER_CONSTRAINT, + COMMANDTAG_ALTER_CONVERSION, + COMMANDTAG_ALTER_DATABASE, + COMMANDTAG_ALTER_DEFAULT_PRIVILEGES, + COMMANDTAG_ALTER_DOMAIN, [...]
I'm a bit worried that this basically duplicates a good portion of NodeTag, without having otherwise much of a point?
From a70b0cadc1142e92b2354a0ca3cd47aaeb0c148e Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Tue, 4 Feb 2020 12:25:05 -0800
Subject: [PATCH v2 2/3] Using a Bitmapset of tags rather than a string array.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bitEventTriggerCacheItem no longer holds an array of palloc’d tag strings
in sorted order, but rather just a Bitmapset over the CommandTags. This
makes the code a little simpler and easier to read, in my opinion. In
filter_event_trigger, rather than running bsearch through a sorted array
of strings, it just runs bms_is_member.
---
It seems weird to add the bsearch just to remove it immediately again a
patch later. This probably should just go first?
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql index 346168673d..cad02212ad 100644 --- a/src/test/regress/sql/event_trigger.sql +++ b/src/test/regress/sql/event_trigger.sql @@ -10,6 +10,13 @@ BEGIN END $$ language plpgsql;+-- OK +create function test_event_trigger2() returns event_trigger as $$ +BEGIN + RAISE NOTICE 'test_event_trigger2: % %', tg_event, tg_tag; +END +$$ LANGUAGE plpgsql; + -- should fail, event triggers cannot have declared arguments create function test_event_trigger_arg(name text) returns event_trigger as $$ BEGIN RETURN 1; END $$ language plpgsql; @@ -82,6 +89,783 @@ create event trigger regress_event_trigger2 on ddl_command_start -- OK comment on event trigger regress_event_trigger is 'test comment';+-- These are all unsupported +create event trigger regress_event_triger_NULL on ddl_command_start + when tag in ('') + execute procedure test_event_trigger2(); + +create event trigger regress_event_triger_UNKNOWN on ddl_command_start + when tag in ('???') + execute procedure test_event_trigger2(); + +create event trigger regress_event_trigger_ALTER_DATABASE on ddl_command_start + when tag in ('ALTER DATABASE') + execute procedure test_event_trigger2();
[...]
There got to be a more maintainable way to write this.
Greetings,
Andres Freund
On Feb 4, 2020, at 7:34 PM, Andres Freund <andres@anarazel.de> wrote:
Hi,
Thanks for reviewing! I am pretty much in agreement with your comments, below.
On 2020-02-04 18:18:52 -0800, Mark Dilger wrote:
In master, a number of functions pass a char *completionTag argument (really a char completionTag[COMPLETION_TAG_BUFSIZE]) which gets filled in with the string to return to the client from EndCommand. I have removed that kind of logic:
- /* save the rowcount if we're given a completionTag to fill */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);In the patch, this is replaced with a new struct, QueryCompletionData. That bit of code above is replaced with:
+ /* save the rowcount if we're given a qc to fill */ + if (qc) + SetQC(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed, DISPLAYFORMAT_NPROCESSED);For wire protocol compatibility, we have to track the display format.
When this gets to EndCommand, the display format allows the string to
be written exactly as the client will expect. If we ever get to the
point where we can break with that compatibility, the third member of
this struct, display_format, can be removed.Hm. While I like not having this as strings a lot, I wish we could get
rid of this displayformat stuff.
Agreed, but I don’t know how.
These are replaced by switch() case statements over the possible commandTags:
+ switch (commandTag) + { + /* + * Supported idiosyncratic special cases. + */ + case COMMANDTAG_ALTER_DEFAULT_PRIVILEGES: + case COMMANDTAG_ALTER_LARGE_OBJECT: + case COMMANDTAG_COMMENT: + case COMMANDTAG_CREATE_TABLE_AS: + case COMMANDTAG_DROP_OWNED: + case COMMANDTAG_GRANT: + case COMMANDTAG_IMPORT_FOREIGN_SCHEMA: + case COMMANDTAG_REFRESH_MATERIALIZED_VIEW: + case COMMANDTAG_REVOKE: + case COMMANDTAG_SECURITY_LABEL: + case COMMANDTAG_SELECT_INTO:The number of these makes me wonder if we should just have a metadata
table in one place, instead of needing to edit multiple
locations. Something likeconst ... CommandTagBehaviour[] = {
[COMMANDTAG_INSERT] = {
.display_processed = true, .display_oid = true, ...},
[COMMANDTAG_CREATE_TABLE_AS] = {
.event_trigger = true, ...},with the zero initialized defaults being the common cases.
Not sure if it's worth going there. But it's maybe worth thinking about
for a minute?
Yes, I was thinking about something like this, only I had in mind a Bitmapset for these. It just so happens that there are 192 enum values, 0..191, which happens to fit in 3 64bit words plus a varlena header. Mind you, that nice property would be immediately blown if we added another entry to the enum. Has anybody made a compile-time static version of Bitmapset? We could store this information in either 24 bytes or 32 bytes, depending on whether we add another enum value.
Getting a little off topic, I was also thinking about having a counting Bitmapset that would store one bit per enum that is included, and then a sparse array of counts, perhaps with one byte counts for [0..127] and 8 byte counts for [128..huge] that we could use in shared memory for the pg_stat_tag work. Is there anything like that?
Anyway, I don’t think we should invent lots of different structures for CommandTag tracking, so something that serves double duty might keep the code tighter. I’m already using ordinary Bitmapset over CommandTags in event_trigger, so naturally that comes to mind for this, too.
Averages for test set 1 by scale:
set scale tps avg_latency 90%< max_latency
1 1 3741 1.734 3.162 133.718
1 9 6124 0.904 1.05 230.547
1 81 5921 0.931 1.015 67.023Averages for test set 1 by clients:
set clients tps avg_latency 90%< max_latency
1 1 2163 0.461 0.514 24.414
1 4 5968 0.675 0.791 40.354
1 16 7655 2.433 3.922 366.519For command tag patch (branched from 1fd687a035):
postgresql % find src -type f -name "*.c" -or -name "*.h" | xargs cat | wc
1482969 5691908 45281399postgresql % find src -type f -name "*.o" | xargs cat | wc
38209 476243 18999752Averages for test set 1 by scale:
set scale tps avg_latency 90%< max_latency
1 1 3877 1.645 3.066 24.973
1 9 6383 0.859 1.032 64.566
1 81 5945 0.925 1.023 162.9Averages for test set 1 by clients:
set clients tps avg_latency 90%< max_latency
1 1 2141 0.466 0.522 11.531
1 4 5967 0.673 0.783 136.882
1 16 8096 2.292 3.817 104.026Not bad.
I still need to get a benchmark more targeted at this codepath.
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 9aa2b61600..5322c14ce4 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -594,7 +594,7 @@ pg_notify(PG_FUNCTION_ARGS) payload = text_to_cstring(PG_GETARG_TEXT_PP(1));/* For NOTIFY as a statement, this is checked in ProcessUtility */ - PreventCommandDuringRecovery("NOTIFY"); + PreventCommandDuringRecovery(COMMANDTAG_NOTIFY);Async_Notify(channel, payload);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 40a8ec1abd..4828e75bd5 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -1063,7 +1063,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,/* check read-only transaction and parallel mode */ if (XactReadOnly && !rel->rd_islocaltemp) - PreventCommandIfReadOnly("COPY FROM"); + PreventCommandIfReadOnly(COMMANDTAG_COPY_FROM);cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);I'm not sure this really ought to be part of this change - seems like a
somewhat independent change to me. With less obvious benefits.
I don’t think I care too much either way. I had some vague ideas about consolidating all of these strings in the backend into one place.
static event_trigger_command_tag_check_result -check_ddl_tag(const char *tag) +check_ddl_tag(CommandTag commandTag) { - const char *obtypename; - const event_trigger_support_data *etsd; + switch (commandTag) + { + /* + * Supported idiosyncratic special cases. + */ + case COMMANDTAG_ALTER_DEFAULT_PRIVILEGES: + case COMMANDTAG_ALTER_LARGE_OBJECT: + case COMMANDTAG_COMMENT: + case COMMANDTAG_CREATE_TABLE_AS: + case COMMANDTAG_DROP_OWNED: + case COMMANDTAG_GRANT: + case COMMANDTAG_IMPORT_FOREIGN_SCHEMA: + case COMMANDTAG_REFRESH_MATERIALIZED_VIEW: + case COMMANDTAG_REVOKE: + case COMMANDTAG_SECURITY_LABEL: + case COMMANDTAG_SELECT_INTO:- /* - * Handle some idiosyncratic special cases. - */ - if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 || - pg_strcasecmp(tag, "SELECT INTO") == 0 || - pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 || - pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 || - pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 || - pg_strcasecmp(tag, "COMMENT") == 0 || - pg_strcasecmp(tag, "GRANT") == 0 || - pg_strcasecmp(tag, "REVOKE") == 0 || - pg_strcasecmp(tag, "DROP OWNED") == 0 || - pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 || - pg_strcasecmp(tag, "SECURITY LABEL") == 0) - return EVENT_TRIGGER_COMMAND_TAG_OK; + /* + * Supported CREATE commands + */ + case COMMANDTAG_CREATE_ACCESS_METHOD: + case COMMANDTAG_CREATE_AGGREGATE: + case COMMANDTAG_CREATE_CAST: + case COMMANDTAG_CREATE_COLLATION: + case COMMANDTAG_CREATE_CONSTRAINT: + case COMMANDTAG_CREATE_CONVERSION: + case COMMANDTAG_CREATE_DOMAIN: + case COMMANDTAG_CREATE_EXTENSION: + case COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER: + case COMMANDTAG_CREATE_FOREIGN_TABLE: + case COMMANDTAG_CREATE_FUNCTION: + case COMMANDTAG_CREATE_INDEX: + case COMMANDTAG_CREATE_LANGUAGE: + case COMMANDTAG_CREATE_MATERIALIZED_VIEW: + case COMMANDTAG_CREATE_OPERATOR: + case COMMANDTAG_CREATE_OPERATOR_CLASS: + case COMMANDTAG_CREATE_OPERATOR_FAMILY: + case COMMANDTAG_CREATE_POLICY: + case COMMANDTAG_CREATE_PROCEDURE: + case COMMANDTAG_CREATE_PUBLICATION: + case COMMANDTAG_CREATE_ROUTINE: + case COMMANDTAG_CREATE_RULE: + case COMMANDTAG_CREATE_SCHEMA: + case COMMANDTAG_CREATE_SEQUENCE: + case COMMANDTAG_CREATE_SERVER: + case COMMANDTAG_CREATE_STATISTICS: + case COMMANDTAG_CREATE_SUBSCRIPTION: + case COMMANDTAG_CREATE_TABLE: + case COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION: + case COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY: + case COMMANDTAG_CREATE_TEXT_SEARCH_PARSER: + case COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE: + case COMMANDTAG_CREATE_TRANSFORM: + case COMMANDTAG_CREATE_TRIGGER: + case COMMANDTAG_CREATE_TYPE: + case COMMANDTAG_CREATE_USER_MAPPING: + case COMMANDTAG_CREATE_VIEW:- /* - * Otherwise, command should be CREATE, ALTER, or DROP. - */ - if (pg_strncasecmp(tag, "CREATE ", 7) == 0) - obtypename = tag + 7; - else if (pg_strncasecmp(tag, "ALTER ", 6) == 0) - obtypename = tag + 6; - else if (pg_strncasecmp(tag, "DROP ", 5) == 0) - obtypename = tag + 5; - else - return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED; + /* + * Supported ALTER commands + */ + case COMMANDTAG_ALTER_ACCESS_METHOD: + case COMMANDTAG_ALTER_AGGREGATE: + case COMMANDTAG_ALTER_CAST: + case COMMANDTAG_ALTER_COLLATION: + case COMMANDTAG_ALTER_CONSTRAINT: + case COMMANDTAG_ALTER_CONVERSION: + case COMMANDTAG_ALTER_DOMAIN: + case COMMANDTAG_ALTER_EXTENSION: + case COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER: + case COMMANDTAG_ALTER_FOREIGN_TABLE: + case COMMANDTAG_ALTER_FUNCTION: + case COMMANDTAG_ALTER_INDEX: + case COMMANDTAG_ALTER_LANGUAGE: + case COMMANDTAG_ALTER_MATERIALIZED_VIEW: + case COMMANDTAG_ALTER_OPERATOR: + case COMMANDTAG_ALTER_OPERATOR_CLASS: + case COMMANDTAG_ALTER_OPERATOR_FAMILY: + case COMMANDTAG_ALTER_POLICY: + case COMMANDTAG_ALTER_PROCEDURE: + case COMMANDTAG_ALTER_PUBLICATION: + case COMMANDTAG_ALTER_ROUTINE: + case COMMANDTAG_ALTER_RULE: + case COMMANDTAG_ALTER_SCHEMA: + case COMMANDTAG_ALTER_SEQUENCE: + case COMMANDTAG_ALTER_SERVER: + case COMMANDTAG_ALTER_STATISTICS: + case COMMANDTAG_ALTER_SUBSCRIPTION: + case COMMANDTAG_ALTER_TABLE: + case COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION: + case COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY: + case COMMANDTAG_ALTER_TEXT_SEARCH_PARSER: + case COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE: + case COMMANDTAG_ALTER_TRANSFORM: + case COMMANDTAG_ALTER_TRIGGER: + case COMMANDTAG_ALTER_TYPE: + case COMMANDTAG_ALTER_USER_MAPPING: + case COMMANDTAG_ALTER_VIEW:- /* - * ...and the object type should be something recognizable. - */ - for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++) - if (pg_strcasecmp(etsd->obtypename, obtypename) == 0) + /* + * Supported DROP commands + */ + case COMMANDTAG_DROP_ACCESS_METHOD: + case COMMANDTAG_DROP_AGGREGATE: + case COMMANDTAG_DROP_CAST: + case COMMANDTAG_DROP_COLLATION: + case COMMANDTAG_DROP_CONSTRAINT: + case COMMANDTAG_DROP_CONVERSION: + case COMMANDTAG_DROP_DOMAIN: + case COMMANDTAG_DROP_EXTENSION: + case COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER: + case COMMANDTAG_DROP_FOREIGN_TABLE: + case COMMANDTAG_DROP_FUNCTION: + case COMMANDTAG_DROP_INDEX: + case COMMANDTAG_DROP_LANGUAGE: + case COMMANDTAG_DROP_MATERIALIZED_VIEW: + case COMMANDTAG_DROP_OPERATOR: + case COMMANDTAG_DROP_OPERATOR_CLASS: + case COMMANDTAG_DROP_OPERATOR_FAMILY: + case COMMANDTAG_DROP_POLICY: + case COMMANDTAG_DROP_PROCEDURE: + case COMMANDTAG_DROP_PUBLICATION: + case COMMANDTAG_DROP_ROUTINE: + case COMMANDTAG_DROP_RULE: + case COMMANDTAG_DROP_SCHEMA: + case COMMANDTAG_DROP_SEQUENCE: + case COMMANDTAG_DROP_SERVER: + case COMMANDTAG_DROP_STATISTICS: + case COMMANDTAG_DROP_SUBSCRIPTION: + case COMMANDTAG_DROP_TABLE: + case COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION: + case COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY: + case COMMANDTAG_DROP_TEXT_SEARCH_PARSER: + case COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE: + case COMMANDTAG_DROP_TRANSFORM: + case COMMANDTAG_DROP_TRIGGER: + case COMMANDTAG_DROP_TYPE: + case COMMANDTAG_DROP_USER_MAPPING: + case COMMANDTAG_DROP_VIEW: + return EVENT_TRIGGER_COMMAND_TAG_OK; + + /* + * Unsupported CREATE commands + */ + case COMMANDTAG_CREATE_DATABASE: + case COMMANDTAG_CREATE_EVENT_TRIGGER: + case COMMANDTAG_CREATE_ROLE: + case COMMANDTAG_CREATE_TABLESPACE: + + /* + * Unsupported ALTER commands + */ + case COMMANDTAG_ALTER_DATABASE: + case COMMANDTAG_ALTER_EVENT_TRIGGER: + case COMMANDTAG_ALTER_ROLE: + case COMMANDTAG_ALTER_TABLESPACE: + + /* + * Unsupported DROP commands + */ + case COMMANDTAG_DROP_DATABASE: + case COMMANDTAG_DROP_EVENT_TRIGGER: + case COMMANDTAG_DROP_ROLE: + case COMMANDTAG_DROP_TABLESPACE: + + /* + * Other unsupported commands. These used to return + * EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED prior to the + * conversion of commandTag from string to enum. + */ + case COMMANDTAG_ALTER_SYSTEM: + case COMMANDTAG_ANALYZE: + case COMMANDTAG_BEGIN: + case COMMANDTAG_CALL: + case COMMANDTAG_CHECKPOINT: + case COMMANDTAG_CLOSE: + case COMMANDTAG_CLOSE_CURSOR: + case COMMANDTAG_CLOSE_CURSOR_ALL: + case COMMANDTAG_CLUSTER: + case COMMANDTAG_COMMIT: + case COMMANDTAG_COMMIT_PREPARED: + case COMMANDTAG_COPY: + case COMMANDTAG_COPY_FROM: + case COMMANDTAG_DEALLOCATE: + case COMMANDTAG_DEALLOCATE_ALL: + case COMMANDTAG_DECLARE_CURSOR: + case COMMANDTAG_DELETE: + case COMMANDTAG_DISCARD: + case COMMANDTAG_DISCARD_ALL: + case COMMANDTAG_DISCARD_PLANS: + case COMMANDTAG_DISCARD_SEQUENCES: + case COMMANDTAG_DISCARD_TEMP: + case COMMANDTAG_DO: + case COMMANDTAG_DROP_REPLICATION_SLOT: + case COMMANDTAG_EXECUTE: + case COMMANDTAG_EXPLAIN: + case COMMANDTAG_FETCH: + case COMMANDTAG_GRANT_ROLE: + case COMMANDTAG_INSERT: + case COMMANDTAG_LISTEN: + case COMMANDTAG_LOAD: + case COMMANDTAG_LOCK_TABLE: + case COMMANDTAG_MOVE: + case COMMANDTAG_NOTIFY: + case COMMANDTAG_PREPARE: + case COMMANDTAG_PREPARE_TRANSACTION: + case COMMANDTAG_REASSIGN_OWNED: + case COMMANDTAG_REINDEX: + case COMMANDTAG_RELEASE: + case COMMANDTAG_RESET: + case COMMANDTAG_REVOKE_ROLE: + case COMMANDTAG_ROLLBACK: + case COMMANDTAG_ROLLBACK_PREPARED: + case COMMANDTAG_SAVEPOINT: + case COMMANDTAG_SELECT: + case COMMANDTAG_SELECT_FOR_KEY_SHARE: + case COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE: + case COMMANDTAG_SELECT_FOR_SHARE: + case COMMANDTAG_SELECT_FOR_UPDATE: + case COMMANDTAG_SET: + case COMMANDTAG_SET_CONSTRAINTS: + case COMMANDTAG_SHOW: + case COMMANDTAG_START_TRANSACTION: + case COMMANDTAG_TRUNCATE_TABLE: + case COMMANDTAG_UNLISTEN: + case COMMANDTAG_UPDATE: + case COMMANDTAG_VACUUM: + return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED; + case COMMANDTAG_UNKNOWN: break; - if (etsd->obtypename == NULL) - return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED; - if (!etsd->supported) - return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED; - return EVENT_TRIGGER_COMMAND_TAG_OK; + } + return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED; }This is pretty painful.
I think it is painful in a different way. The existing code on master is a mess of parsing logic that is harder to reason through, but fewer lines. There are other places in the backend that have long switch statements, so I didn’t feel I was breaking with project style to do this. I also made the switch longer than I had too, by including all enumerated values rather than just the ones that are supported. We could remove the extra cases, but I think that’s only a half measure. Something more like a consolidated table or bitmap seems better.
@@ -745,7 +902,7 @@ EventTriggerCommonSetup(Node *parsetree,
return NIL;/* Get the command tag. */ - tag = CreateCommandTag(parsetree); + tag = GetCommandTagName(CreateCommandTag(parsetree));/* * Filter list of event triggers by command tag, and copy them into our @@ -2136,7 +2293,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS) /* objsubid */ values[i++] = Int32GetDatum(addr.objectSubId); /* command tag */ - values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree)); + values[i++] = CStringGetTextDatum(GetCommandTagName(CreateCommandTag(cmd->parsetree))); /* object_type */ values[i++] = CStringGetTextDatum(type); /* schema */ @@ -2161,7 +2318,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS) /* objsubid */ nulls[i++] = true; /* command tag */ - values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree)); + values[i++] = CStringGetTextDatum(GetCommandTagName(CreateCommandTag(cmd->parsetree))); /* object_type */ values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype)); /* schema */So GetCommandTagName we commonly do twice for some reason? Once in
EventTriggerCommonSetup() and then again in
pg_event_trigger_ddl_commands()? Why is EventTriggerData.tag still the
string?
It is not a string after applying v2-0003…. The main issue I see in the code you are quoting is that CreateCommandTag(cmd->parsetree) is called more than once, and that’s not the consequence of this patch. That’s pre-existing. I didn’t look into it, though I can if you think it is relevant to this patch set. The name of the function, CreateCommandTag, sounds like something I invented as part of this patch, but it pre-exists this patch. I only changed it’s return value from char * to CommandTag.
Assert(list_length(plan->plancache_list) == 1); @@ -1469,7 +1469,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /* translator: %s is a SQL statement name */ errmsg("%s is not allowed in a non-volatile function", - CreateCommandTag((Node *) pstmt)))); + GetCommandTagName(CreateCommandTag((Node *) pstmt)))));Probably worth having a wrapper for this - these lines are pretty long,
and there quite a number of cases like it in the patch.
Actually, I looked at that. The number of them seemed right on the line between making a wrapper and not. I thought the counter-argument was that by making a wrapper that only got used in a few places, I was creating more lines of code and obfuscating what happens. I’m happy to do it your way, if consensus emerges around that.
@@ -172,11 +175,38 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestRemoteSimple:/* - * We assume the commandTag is plain ASCII and therefore requires - * no encoding conversion. + * We assume the tagname is plain ASCII and therefore + * requires no encoding conversion. */ - pq_putmessage('C', commandTag, strlen(commandTag) + 1); - break; + tagname = GetCommandTagName(qc->commandTag); + switch (qc->display_format) + { + case DISPLAYFORMAT_PLAIN: + pq_putmessage('C', tagname, strlen(tagname) + 1); + break; + case DISPLAYFORMAT_LAST_OID: + /* + * We no longer display LastOid, but to preserve the wire protocol, + * we write InvalidOid where the LastOid used to be written. For + * efficiency in the snprintf(), hard-code InvalidOid as zero. + */ + Assert(InvalidOid == 0); + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, + "%s 0 " UINT64_FORMAT, + tagname, + qc->nprocessed); + pq_putmessage('C', completionTag, strlen(completionTag) + 1); + break; + case DISPLAYFORMAT_NPROCESSED: + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, + "%s " UINT64_FORMAT, + tagname, + qc->nprocessed); + pq_putmessage('C', completionTag, strlen(completionTag) + 1); + break; + default: + elog(ERROR, "Invalid display_format in EndCommand"); + }Imo there should only be one pq_putmessage(). Also think this type of
default: is a bad idea, just prevents the compiler from warning if we
were to ever introduce a new variant of DISPLAYFORMAT_*, without
providing any meaningful additional security.
Ok.
@@ -855,7 +889,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case T_DiscardStmt: /* should we allow DISCARD PLANS? */ - CheckRestrictedOperation("DISCARD"); + CheckRestrictedOperation(COMMANDTAG_DISCARD); DiscardCommand((DiscardStmt *) parsetree, isTopLevel); break;@@ -974,7 +1008,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecuteGrantStmt(stmt);
}
@@ -987,7 +1021,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecDropStmt(stmt, isTopLevel);
}
@@ -1000,7 +1034,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecRenameStmt(stmt);
}
@@ -1013,7 +1047,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectDependsStmt(stmt, NULL);
}
@@ -1026,7 +1060,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
}
@@ -1039,7 +1073,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterOwnerStmt(stmt);
}
@@ -1052,7 +1086,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
CommentObject(stmt);
break;
@@ -1065,7 +1099,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);Not this patch's fault or task. But I hate this type of code - needing
to touch a dozen places for new type of statement is just
insane. utility.c should long have been rewritten to just have one
metadata table for nearly all of this. Perhaps with a few callbacks for
special cases.
No objection from me, though I’d have to see the alternative and what it does to performance.
+static const char * tag_names[] = { + "???", + "ALTER ACCESS METHOD", + "ALTER AGGREGATE", + "ALTER CAST",This seems problematic to maintain, because the order needs to match
between this and something defined in a header - and there's no
guarantee a misordering is immediately noticeable. We should either go
for my metadata table idea, or at least rewrite this, even if more
verbose, to something likestatic const char * tag_names[] = {
[COMMAND_TAG_ALTER_ACCESS_METHOD] = "ALTER ACCESS METHOD",
...I think the fact that this would show up in a grep for
COMMAND_TAG_ALTER_ACCESS_METHOD is good too.
I had something closer to what you’re asking for as part of the v1 patch and ripped it out to get the code size down. Avoiding code bloat was one of Tom's concerns. What you are suggesting is admittedly better than what I ripped out, though.
+/* + * Search CommandTag by name + * + * Returns CommandTag, or COMMANDTAG_UNKNOWN if not recognized + */ +CommandTag +GetCommandTagEnum(const char *commandname) +{ + const char **base, **last, **position; + int result; + + OPTIONALLY_CHECK_COMMAND_TAGS(); + if (commandname == NULL || *commandname == '\0') + return COMMANDTAG_UNKNOWN; + + base = tag_names; + last = tag_names + tag_name_length - 1; + while (last >= base) + { + position = base + ((last - base) >> 1); + result = pg_strcasecmp(commandname, *position); + if (result == 0) + return (CommandTag) (position - tag_names); + else if (result < 0) + last = position - 1; + else + base = position + 1; + } + return COMMANDTAG_UNKNOWN; +}This seems pretty grotty - but you get rid of it later. See my comments there.
+#ifdef COMMANDTAG_CHECKING +bool +CheckCommandTagEnum() +{ + CommandTag i, j; + + if (FIRST_COMMAND_TAG < 0 || LAST_COMMAND_TAG < 0 || LAST_COMMAND_TAG < FIRST_COMMAND_TAG) + { + elog(ERROR, "FIRST_COMMAND_TAG (%u), LAST_COMMAND_TAG (%u) not reasonable", + (unsigned int) FIRST_COMMAND_TAG, (unsigned int) LAST_COMMAND_TAG); + return false; + } + if (FIRST_COMMAND_TAG != (CommandTag)0) + { + elog(ERROR, "FIRST_COMMAND_TAG (%u) != 0", (unsigned int) FIRST_COMMAND_TAG); + return false; + } + if (LAST_COMMAND_TAG != (CommandTag)(tag_name_length - 1)) + { + elog(ERROR, "LAST_COMMAND_TAG (%u) != tag_name_length (%u)", + (unsigned int) LAST_COMMAND_TAG, (unsigned int) tag_name_length); + return false; + }These all seem to want to be static asserts.
+ for (i = FIRST_COMMAND_TAG; i < LAST_COMMAND_TAG; i++) + { + for (j = i+1; j < LAST_COMMAND_TAG; j++) + { + int cmp = strcmp(tag_names[i], tag_names[j]); + if (cmp == 0) + { + elog(ERROR, "Found duplicate tag_name: \"%s\"", + tag_names[i]); + return false; + } + if (cmp > 0) + { + elog(ERROR, "Found commandnames out of order: \"%s\" before \"%s\"", + tag_names[i], tag_names[j]); + return false; + } + } + } + return true; +}And I think we could get rid of this with my earlier suggestions?
+/* + * BEWARE: These are in sorted order, but ordered by their printed + * values in the tag_name list (see common/commandtag.c). + * In particular it matters because the sort ordering changes + * when you replace a space with an underscore. To wit: + * + * "CREATE TABLE" + * "CREATE TABLE AS" + * "CREATE TABLESPACE" + * + * but... + * + * CREATE_TABLE + * CREATE_TABLESPACE + * CREATE_TABLE_AS + * + * It also matters that COMMANDTAG_UNKNOWN is written "???". + * + * If you add a value here, add it in common/commandtag.c also, and + * be careful to get the ordering right. You can build with + * COMMANDTAG_CHECKING to have this automatically checked + * at runtime, but that adds considerable overhead, so do so sparingly. + */ +typedef enum CommandTag +{This seems pretty darn nightmarish.
+#define FIRST_COMMAND_TAG COMMANDTAG_UNKNOWN + COMMANDTAG_UNKNOWN, + COMMANDTAG_ALTER_ACCESS_METHOD, + COMMANDTAG_ALTER_AGGREGATE, + COMMANDTAG_ALTER_CAST, + COMMANDTAG_ALTER_COLLATION, + COMMANDTAG_ALTER_CONSTRAINT, + COMMANDTAG_ALTER_CONVERSION, + COMMANDTAG_ALTER_DATABASE, + COMMANDTAG_ALTER_DEFAULT_PRIVILEGES, + COMMANDTAG_ALTER_DOMAIN, [...]I'm a bit worried that this basically duplicates a good portion of NodeTag, without having otherwise much of a point?
I never quite came up with a one-size-fits-all enumeration. There are lots of places where these enumerations seem to almost map onto each other, but with special cases that don’t line up. I’m open to suggestions.
From a70b0cadc1142e92b2354a0ca3cd47aaeb0c148e Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Tue, 4 Feb 2020 12:25:05 -0800
Subject: [PATCH v2 2/3] Using a Bitmapset of tags rather than a string array.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bitEventTriggerCacheItem no longer holds an array of palloc’d tag strings
in sorted order, but rather just a Bitmapset over the CommandTags. This
makes the code a little simpler and easier to read, in my opinion. In
filter_event_trigger, rather than running bsearch through a sorted array
of strings, it just runs bms_is_member.
---It seems weird to add the bsearch just to remove it immediately again a
patch later. This probably should just go first?
I’m not sure what you mean. That bsearch is pre-existing, not mine.
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql index 346168673d..cad02212ad 100644 --- a/src/test/regress/sql/event_trigger.sql +++ b/src/test/regress/sql/event_trigger.sql @@ -10,6 +10,13 @@ BEGIN END $$ language plpgsql;+-- OK +create function test_event_trigger2() returns event_trigger as $$ +BEGIN + RAISE NOTICE 'test_event_trigger2: % %', tg_event, tg_tag; +END +$$ LANGUAGE plpgsql; + -- should fail, event triggers cannot have declared arguments create function test_event_trigger_arg(name text) returns event_trigger as $$ BEGIN RETURN 1; END $$ language plpgsql; @@ -82,6 +89,783 @@ create event trigger regress_event_trigger2 on ddl_command_start -- OK comment on event trigger regress_event_trigger is 'test comment';+-- These are all unsupported +create event trigger regress_event_triger_NULL on ddl_command_start + when tag in ('') + execute procedure test_event_trigger2(); + +create event trigger regress_event_triger_UNKNOWN on ddl_command_start + when tag in ('???') + execute procedure test_event_trigger2(); + +create event trigger regress_event_trigger_ALTER_DATABASE on ddl_command_start + when tag in ('ALTER DATABASE') + execute procedure test_event_trigger2();[...]
There got to be a more maintainable way to write this.
Yeah, I already conceded to Tom in his review that I’m not wedded to committing this test in any form, let alone in this form. That’s part of why I kept it as a separate patch file. But if you like what it is doing, and just don’t like the verbosity, I can try harder to compress it.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Andres,
The previous patch set seemed to cause confusion, having separated changes into multiple files. The latest patch, heavily influenced by your review, is all in one file, attached.
On Feb 4, 2020, at 7:34 PM, Andres Freund <andres@anarazel.de> wrote:
These are replaced by switch() case statements over the possible commandTags:
+ switch (commandTag) + { + /* + * Supported idiosyncratic special cases. + */ + case COMMANDTAG_ALTER_DEFAULT_PRIVILEGES: + case COMMANDTAG_ALTER_LARGE_OBJECT: + case COMMANDTAG_COMMENT: + case COMMANDTAG_CREATE_TABLE_AS: + case COMMANDTAG_DROP_OWNED: + case COMMANDTAG_GRANT: + case COMMANDTAG_IMPORT_FOREIGN_SCHEMA: + case COMMANDTAG_REFRESH_MATERIALIZED_VIEW: + case COMMANDTAG_REVOKE: + case COMMANDTAG_SECURITY_LABEL: + case COMMANDTAG_SELECT_INTO:The number of these makes me wonder if we should just have a metadata
table in one place, instead of needing to edit multiple
locations. Something likeconst ... CommandTagBehaviour[] = {
[COMMANDTAG_INSERT] = {
.display_processed = true, .display_oid = true, ...},
[COMMANDTAG_CREATE_TABLE_AS] = {
.event_trigger = true, ...},with the zero initialized defaults being the common cases.
Not sure if it's worth going there. But it's maybe worth thinking about
for a minute?
The v3 patch does something like you suggest.
The only gotcha I came across while reorganizing the code this way is that exec_replication_command(…) outputs “SELECT” rather than “SELECT <ROWCOUNT>” as is done everywhere else. Strangely, exec_replication_command(…) does output the rowcount for “COPY <ROWCOUNT>”, which matches how COPY is handled elsewhere. I can’t see any logic in this. I’m concerned that outputting “SELECT 0” from exec_replication_command rather than “SELECT” as is currently done will break some client somewhere, though none that I can find.
Putting the display information into the CommandTag behavior table forces the behavior per tag to be the same everywhere, which forces this change on exec_replication_command.
To get around this, I’ve added an extremely bogus extra boolean argument to EndCommand, force_undecorated_output, that is false from all callers except exec_replication_command(…) in the one spot I described.
I don’t know whether the code should be committed this way, but I need something as a placeholder until I get a better understanding of why exec_replication_command(…) behaves as it does and what I should do about it in the patch.
Averages for test set 1 by scale:
set scale tps avg_latency 90%< max_latency
1 1 3741 1.734 3.162 133.718
1 9 6124 0.904 1.05 230.547
1 81 5921 0.931 1.015 67.023Averages for test set 1 by clients:
set clients tps avg_latency 90%< max_latency
1 1 2163 0.461 0.514 24.414
1 4 5968 0.675 0.791 40.354
1 16 7655 2.433 3.922 366.519For command tag patch (branched from 1fd687a035):
postgresql % find src -type f -name "*.c" -or -name "*.h" | xargs cat | wc
1482969 5691908 45281399postgresql % find src -type f -name "*.o" | xargs cat | wc
38209 476243 18999752Averages for test set 1 by scale:
set scale tps avg_latency 90%< max_latency
1 1 3877 1.645 3.066 24.973
1 9 6383 0.859 1.032 64.566
1 81 5945 0.925 1.023 162.9Averages for test set 1 by clients:
set clients tps avg_latency 90%< max_latency
1 1 2141 0.466 0.522 11.531
1 4 5967 0.673 0.783 136.882
1 16 8096 2.292 3.817 104.026Not bad.
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 9aa2b61600..5322c14ce4 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -594,7 +594,7 @@ pg_notify(PG_FUNCTION_ARGS) payload = text_to_cstring(PG_GETARG_TEXT_PP(1));/* For NOTIFY as a statement, this is checked in ProcessUtility */ - PreventCommandDuringRecovery("NOTIFY"); + PreventCommandDuringRecovery(COMMANDTAG_NOTIFY);Async_Notify(channel, payload);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 40a8ec1abd..4828e75bd5 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -1063,7 +1063,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,/* check read-only transaction and parallel mode */ if (XactReadOnly && !rel->rd_islocaltemp) - PreventCommandIfReadOnly("COPY FROM"); + PreventCommandIfReadOnly(COMMANDTAG_COPY_FROM);cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);I'm not sure this really ought to be part of this change - seems like a
somewhat independent change to me. With less obvious benefits.
This is changed back in v3 to be more like how it was before.
static event_trigger_command_tag_check_result -check_ddl_tag(const char *tag) +check_ddl_tag(CommandTag commandTag) { - const char *obtypename; - const event_trigger_support_data *etsd; + switch (commandTag) + { + /* + * Supported idiosyncratic special cases. + */ + case COMMANDTAG_ALTER_DEFAULT_PRIVILEGES: + case COMMANDTAG_ALTER_LARGE_OBJECT: + case COMMANDTAG_COMMENT: + case COMMANDTAG_CREATE_TABLE_AS: + case COMMANDTAG_DROP_OWNED: + case COMMANDTAG_GRANT: + case COMMANDTAG_IMPORT_FOREIGN_SCHEMA: + case COMMANDTAG_REFRESH_MATERIALIZED_VIEW: + case COMMANDTAG_REVOKE: + case COMMANDTAG_SECURITY_LABEL: + case COMMANDTAG_SELECT_INTO:- /* - * Handle some idiosyncratic special cases. - */ - if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 || - pg_strcasecmp(tag, "SELECT INTO") == 0 || - pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 || - pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 || - pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 || - pg_strcasecmp(tag, "COMMENT") == 0 || - pg_strcasecmp(tag, "GRANT") == 0 || - pg_strcasecmp(tag, "REVOKE") == 0 || - pg_strcasecmp(tag, "DROP OWNED") == 0 || - pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 || - pg_strcasecmp(tag, "SECURITY LABEL") == 0) - return EVENT_TRIGGER_COMMAND_TAG_OK; + /* + * Supported CREATE commands + */ + case COMMANDTAG_CREATE_ACCESS_METHOD: + case COMMANDTAG_CREATE_AGGREGATE: + case COMMANDTAG_CREATE_CAST: + case COMMANDTAG_CREATE_COLLATION: + case COMMANDTAG_CREATE_CONSTRAINT: + case COMMANDTAG_CREATE_CONVERSION: + case COMMANDTAG_CREATE_DOMAIN: + case COMMANDTAG_CREATE_EXTENSION: + case COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER: + case COMMANDTAG_CREATE_FOREIGN_TABLE: + case COMMANDTAG_CREATE_FUNCTION: + case COMMANDTAG_CREATE_INDEX: + case COMMANDTAG_CREATE_LANGUAGE: + case COMMANDTAG_CREATE_MATERIALIZED_VIEW: + case COMMANDTAG_CREATE_OPERATOR: + case COMMANDTAG_CREATE_OPERATOR_CLASS: + case COMMANDTAG_CREATE_OPERATOR_FAMILY: + case COMMANDTAG_CREATE_POLICY: + case COMMANDTAG_CREATE_PROCEDURE: + case COMMANDTAG_CREATE_PUBLICATION: + case COMMANDTAG_CREATE_ROUTINE: + case COMMANDTAG_CREATE_RULE: + case COMMANDTAG_CREATE_SCHEMA: + case COMMANDTAG_CREATE_SEQUENCE: + case COMMANDTAG_CREATE_SERVER: + case COMMANDTAG_CREATE_STATISTICS: + case COMMANDTAG_CREATE_SUBSCRIPTION: + case COMMANDTAG_CREATE_TABLE: + case COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION: + case COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY: + case COMMANDTAG_CREATE_TEXT_SEARCH_PARSER: + case COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE: + case COMMANDTAG_CREATE_TRANSFORM: + case COMMANDTAG_CREATE_TRIGGER: + case COMMANDTAG_CREATE_TYPE: + case COMMANDTAG_CREATE_USER_MAPPING: + case COMMANDTAG_CREATE_VIEW:- /* - * Otherwise, command should be CREATE, ALTER, or DROP. - */ - if (pg_strncasecmp(tag, "CREATE ", 7) == 0) - obtypename = tag + 7; - else if (pg_strncasecmp(tag, "ALTER ", 6) == 0) - obtypename = tag + 6; - else if (pg_strncasecmp(tag, "DROP ", 5) == 0) - obtypename = tag + 5; - else - return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED; + /* + * Supported ALTER commands + */ + case COMMANDTAG_ALTER_ACCESS_METHOD: + case COMMANDTAG_ALTER_AGGREGATE: + case COMMANDTAG_ALTER_CAST: + case COMMANDTAG_ALTER_COLLATION: + case COMMANDTAG_ALTER_CONSTRAINT: + case COMMANDTAG_ALTER_CONVERSION: + case COMMANDTAG_ALTER_DOMAIN: + case COMMANDTAG_ALTER_EXTENSION: + case COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER: + case COMMANDTAG_ALTER_FOREIGN_TABLE: + case COMMANDTAG_ALTER_FUNCTION: + case COMMANDTAG_ALTER_INDEX: + case COMMANDTAG_ALTER_LANGUAGE: + case COMMANDTAG_ALTER_MATERIALIZED_VIEW: + case COMMANDTAG_ALTER_OPERATOR: + case COMMANDTAG_ALTER_OPERATOR_CLASS: + case COMMANDTAG_ALTER_OPERATOR_FAMILY: + case COMMANDTAG_ALTER_POLICY: + case COMMANDTAG_ALTER_PROCEDURE: + case COMMANDTAG_ALTER_PUBLICATION: + case COMMANDTAG_ALTER_ROUTINE: + case COMMANDTAG_ALTER_RULE: + case COMMANDTAG_ALTER_SCHEMA: + case COMMANDTAG_ALTER_SEQUENCE: + case COMMANDTAG_ALTER_SERVER: + case COMMANDTAG_ALTER_STATISTICS: + case COMMANDTAG_ALTER_SUBSCRIPTION: + case COMMANDTAG_ALTER_TABLE: + case COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION: + case COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY: + case COMMANDTAG_ALTER_TEXT_SEARCH_PARSER: + case COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE: + case COMMANDTAG_ALTER_TRANSFORM: + case COMMANDTAG_ALTER_TRIGGER: + case COMMANDTAG_ALTER_TYPE: + case COMMANDTAG_ALTER_USER_MAPPING: + case COMMANDTAG_ALTER_VIEW:- /* - * ...and the object type should be something recognizable. - */ - for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++) - if (pg_strcasecmp(etsd->obtypename, obtypename) == 0) + /* + * Supported DROP commands + */ + case COMMANDTAG_DROP_ACCESS_METHOD: + case COMMANDTAG_DROP_AGGREGATE: + case COMMANDTAG_DROP_CAST: + case COMMANDTAG_DROP_COLLATION: + case COMMANDTAG_DROP_CONSTRAINT: + case COMMANDTAG_DROP_CONVERSION: + case COMMANDTAG_DROP_DOMAIN: + case COMMANDTAG_DROP_EXTENSION: + case COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER: + case COMMANDTAG_DROP_FOREIGN_TABLE: + case COMMANDTAG_DROP_FUNCTION: + case COMMANDTAG_DROP_INDEX: + case COMMANDTAG_DROP_LANGUAGE: + case COMMANDTAG_DROP_MATERIALIZED_VIEW: + case COMMANDTAG_DROP_OPERATOR: + case COMMANDTAG_DROP_OPERATOR_CLASS: + case COMMANDTAG_DROP_OPERATOR_FAMILY: + case COMMANDTAG_DROP_POLICY: + case COMMANDTAG_DROP_PROCEDURE: + case COMMANDTAG_DROP_PUBLICATION: + case COMMANDTAG_DROP_ROUTINE: + case COMMANDTAG_DROP_RULE: + case COMMANDTAG_DROP_SCHEMA: + case COMMANDTAG_DROP_SEQUENCE: + case COMMANDTAG_DROP_SERVER: + case COMMANDTAG_DROP_STATISTICS: + case COMMANDTAG_DROP_SUBSCRIPTION: + case COMMANDTAG_DROP_TABLE: + case COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION: + case COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY: + case COMMANDTAG_DROP_TEXT_SEARCH_PARSER: + case COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE: + case COMMANDTAG_DROP_TRANSFORM: + case COMMANDTAG_DROP_TRIGGER: + case COMMANDTAG_DROP_TYPE: + case COMMANDTAG_DROP_USER_MAPPING: + case COMMANDTAG_DROP_VIEW: + return EVENT_TRIGGER_COMMAND_TAG_OK; + + /* + * Unsupported CREATE commands + */ + case COMMANDTAG_CREATE_DATABASE: + case COMMANDTAG_CREATE_EVENT_TRIGGER: + case COMMANDTAG_CREATE_ROLE: + case COMMANDTAG_CREATE_TABLESPACE: + + /* + * Unsupported ALTER commands + */ + case COMMANDTAG_ALTER_DATABASE: + case COMMANDTAG_ALTER_EVENT_TRIGGER: + case COMMANDTAG_ALTER_ROLE: + case COMMANDTAG_ALTER_TABLESPACE: + + /* + * Unsupported DROP commands + */ + case COMMANDTAG_DROP_DATABASE: + case COMMANDTAG_DROP_EVENT_TRIGGER: + case COMMANDTAG_DROP_ROLE: + case COMMANDTAG_DROP_TABLESPACE: + + /* + * Other unsupported commands. These used to return + * EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED prior to the + * conversion of commandTag from string to enum. + */ + case COMMANDTAG_ALTER_SYSTEM: + case COMMANDTAG_ANALYZE: + case COMMANDTAG_BEGIN: + case COMMANDTAG_CALL: + case COMMANDTAG_CHECKPOINT: + case COMMANDTAG_CLOSE: + case COMMANDTAG_CLOSE_CURSOR: + case COMMANDTAG_CLOSE_CURSOR_ALL: + case COMMANDTAG_CLUSTER: + case COMMANDTAG_COMMIT: + case COMMANDTAG_COMMIT_PREPARED: + case COMMANDTAG_COPY: + case COMMANDTAG_COPY_FROM: + case COMMANDTAG_DEALLOCATE: + case COMMANDTAG_DEALLOCATE_ALL: + case COMMANDTAG_DECLARE_CURSOR: + case COMMANDTAG_DELETE: + case COMMANDTAG_DISCARD: + case COMMANDTAG_DISCARD_ALL: + case COMMANDTAG_DISCARD_PLANS: + case COMMANDTAG_DISCARD_SEQUENCES: + case COMMANDTAG_DISCARD_TEMP: + case COMMANDTAG_DO: + case COMMANDTAG_DROP_REPLICATION_SLOT: + case COMMANDTAG_EXECUTE: + case COMMANDTAG_EXPLAIN: + case COMMANDTAG_FETCH: + case COMMANDTAG_GRANT_ROLE: + case COMMANDTAG_INSERT: + case COMMANDTAG_LISTEN: + case COMMANDTAG_LOAD: + case COMMANDTAG_LOCK_TABLE: + case COMMANDTAG_MOVE: + case COMMANDTAG_NOTIFY: + case COMMANDTAG_PREPARE: + case COMMANDTAG_PREPARE_TRANSACTION: + case COMMANDTAG_REASSIGN_OWNED: + case COMMANDTAG_REINDEX: + case COMMANDTAG_RELEASE: + case COMMANDTAG_RESET: + case COMMANDTAG_REVOKE_ROLE: + case COMMANDTAG_ROLLBACK: + case COMMANDTAG_ROLLBACK_PREPARED: + case COMMANDTAG_SAVEPOINT: + case COMMANDTAG_SELECT: + case COMMANDTAG_SELECT_FOR_KEY_SHARE: + case COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE: + case COMMANDTAG_SELECT_FOR_SHARE: + case COMMANDTAG_SELECT_FOR_UPDATE: + case COMMANDTAG_SET: + case COMMANDTAG_SET_CONSTRAINTS: + case COMMANDTAG_SHOW: + case COMMANDTAG_START_TRANSACTION: + case COMMANDTAG_TRUNCATE_TABLE: + case COMMANDTAG_UNLISTEN: + case COMMANDTAG_UPDATE: + case COMMANDTAG_VACUUM: + return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED; + case COMMANDTAG_UNKNOWN: break; - if (etsd->obtypename == NULL) - return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED; - if (!etsd->supported) - return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED; - return EVENT_TRIGGER_COMMAND_TAG_OK; + } + return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED; }This is pretty painful.
Yeah, and it’s gone in v3. This sort of logic now lives in the behavior table in src/backend/utils/misc/commandtag.c.
@@ -745,7 +902,7 @@ EventTriggerCommonSetup(Node *parsetree,
return NIL;/* Get the command tag. */ - tag = CreateCommandTag(parsetree); + tag = GetCommandTagName(CreateCommandTag(parsetree));/* * Filter list of event triggers by command tag, and copy them into our @@ -2136,7 +2293,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS) /* objsubid */ values[i++] = Int32GetDatum(addr.objectSubId); /* command tag */ - values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree)); + values[i++] = CStringGetTextDatum(GetCommandTagName(CreateCommandTag(cmd->parsetree))); /* object_type */ values[i++] = CStringGetTextDatum(type); /* schema */ @@ -2161,7 +2318,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS) /* objsubid */ nulls[i++] = true; /* command tag */ - values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree)); + values[i++] = CStringGetTextDatum(GetCommandTagName(CreateCommandTag(cmd->parsetree))); /* object_type */ values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype)); /* schema */So GetCommandTagName we commonly do twice for some reason? Once in
EventTriggerCommonSetup() and then again in
pg_event_trigger_ddl_commands()? Why is EventTriggerData.tag still the
string?
EventTriggerCommonSetup() gets the command tag enum, not the string, at least in v3.
Assert(list_length(plan->plancache_list) == 1); @@ -1469,7 +1469,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /* translator: %s is a SQL statement name */ errmsg("%s is not allowed in a non-volatile function", - CreateCommandTag((Node *) pstmt)))); + GetCommandTagName(CreateCommandTag((Node *) pstmt)))));Probably worth having a wrapper for this - these lines are pretty long,
and there quite a number of cases like it in the patch.
I was having some trouble figuring out what to name the wrapper. “CreateCommandTagAndGetName" is nearly as long as the two function names it replaces. “CreateCommandTagName” sounds like you’re creating a name rather than a CommandTag, which is misleading. But then I realized this function was poorly named to begin with. “Create” is an entirely inappropriate verb for what this function does. Even before this patch, it wasn’t creating anything. I was looking up a constant string name. Now it is looking up an enum.
I went with CreateCommandName(…), but I think this leaves a lot to be desired. Thoughts?
@@ -172,11 +175,38 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestRemoteSimple:/* - * We assume the commandTag is plain ASCII and therefore requires - * no encoding conversion. + * We assume the tagname is plain ASCII and therefore + * requires no encoding conversion. */ - pq_putmessage('C', commandTag, strlen(commandTag) + 1); - break; + tagname = GetCommandTagName(qc->commandTag); + switch (qc->display_format) + { + case DISPLAYFORMAT_PLAIN: + pq_putmessage('C', tagname, strlen(tagname) + 1); + break; + case DISPLAYFORMAT_LAST_OID: + /* + * We no longer display LastOid, but to preserve the wire protocol, + * we write InvalidOid where the LastOid used to be written. For + * efficiency in the snprintf(), hard-code InvalidOid as zero. + */ + Assert(InvalidOid == 0); + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, + "%s 0 " UINT64_FORMAT, + tagname, + qc->nprocessed); + pq_putmessage('C', completionTag, strlen(completionTag) + 1); + break; + case DISPLAYFORMAT_NPROCESSED: + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, + "%s " UINT64_FORMAT, + tagname, + qc->nprocessed); + pq_putmessage('C', completionTag, strlen(completionTag) + 1); + break; + default: + elog(ERROR, "Invalid display_format in EndCommand"); + }Imo there should only be one pq_putmessage(). Also think this type of
default: is a bad idea, just prevents the compiler from warning if we
were to ever introduce a new variant of DISPLAYFORMAT_*, without
providing any meaningful additional security.
This is fixed in v3.
@@ -855,7 +889,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case T_DiscardStmt: /* should we allow DISCARD PLANS? */ - CheckRestrictedOperation("DISCARD"); + CheckRestrictedOperation(COMMANDTAG_DISCARD); DiscardCommand((DiscardStmt *) parsetree, isTopLevel); break;@@ -974,7 +1008,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecuteGrantStmt(stmt);
}
@@ -987,7 +1021,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecDropStmt(stmt, isTopLevel);
}
@@ -1000,7 +1034,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecRenameStmt(stmt);
}
@@ -1013,7 +1047,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectDependsStmt(stmt, NULL);
}
@@ -1026,7 +1060,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
}
@@ -1039,7 +1073,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterOwnerStmt(stmt);
}
@@ -1052,7 +1086,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
CommentObject(stmt);
break;
@@ -1065,7 +1099,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);Not this patch's fault or task. But I hate this type of code - needing
to touch a dozen places for new type of statement is just
insane. utility.c should long have been rewritten to just have one
metadata table for nearly all of this. Perhaps with a few callbacks for
special cases.
I’ve decided not to touch this issue. There are no changes here from how it was done in v2.
+static const char * tag_names[] = { + "???", + "ALTER ACCESS METHOD", + "ALTER AGGREGATE", + "ALTER CAST",This seems problematic to maintain, because the order needs to match
between this and something defined in a header - and there's no
guarantee a misordering is immediately noticeable. We should either go
for my metadata table idea, or at least rewrite this, even if more
verbose, to something likestatic const char * tag_names[] = {
[COMMAND_TAG_ALTER_ACCESS_METHOD] = "ALTER ACCESS METHOD",
...I think the fact that this would show up in a grep for
COMMAND_TAG_ALTER_ACCESS_METHOD is good too.
Rewriting this as you suggest does not prevent tag names from being out of sorted order. Version 3 of the patch adds a perl script that reads commandtag.h and commandtag.c during the build process and stops the build with a brief error message if they don’t match, are malformed, or if the sorting order is wrong. The script does not modify the code. It just reviews it for correctness. As such, it probably doesn’t matter whether it runs on all platforms. I did not look into whether this runs on Windows, but if there is any difficulty there, it could simply be disabled on that platform.
It also doesn’t matter if this perl script gets committed. There is a trade-off here between maintaining the script vs. manually maintaining the enum ordering.
+/* + * Search CommandTag by name + * + * Returns CommandTag, or COMMANDTAG_UNKNOWN if not recognized + */ +CommandTag +GetCommandTagEnum(const char *commandname) +{ + const char **base, **last, **position; + int result; + + OPTIONALLY_CHECK_COMMAND_TAGS(); + if (commandname == NULL || *commandname == '\0') + return COMMANDTAG_UNKNOWN; + + base = tag_names; + last = tag_names + tag_name_length - 1; + while (last >= base) + { + position = base + ((last - base) >> 1); + result = pg_strcasecmp(commandname, *position); + if (result == 0) + return (CommandTag) (position - tag_names); + else if (result < 0) + last = position - 1; + else + base = position + 1; + } + return COMMANDTAG_UNKNOWN; +}This seems pretty grotty - but you get rid of it later. See my comments there.
I kept a form of GetCommandTagEnum.
+#ifdef COMMANDTAG_CHECKING +bool +CheckCommandTagEnum() +{ + CommandTag i, j; + + if (FIRST_COMMAND_TAG < 0 || LAST_COMMAND_TAG < 0 || LAST_COMMAND_TAG < FIRST_COMMAND_TAG) + { + elog(ERROR, "FIRST_COMMAND_TAG (%u), LAST_COMMAND_TAG (%u) not reasonable", + (unsigned int) FIRST_COMMAND_TAG, (unsigned int) LAST_COMMAND_TAG); + return false; + } + if (FIRST_COMMAND_TAG != (CommandTag)0) + { + elog(ERROR, "FIRST_COMMAND_TAG (%u) != 0", (unsigned int) FIRST_COMMAND_TAG); + return false; + } + if (LAST_COMMAND_TAG != (CommandTag)(tag_name_length - 1)) + { + elog(ERROR, "LAST_COMMAND_TAG (%u) != tag_name_length (%u)", + (unsigned int) LAST_COMMAND_TAG, (unsigned int) tag_name_length); + return false; + }These all seem to want to be static asserts.
This is all gone now, either to the perl script or to a StaticAssert, or to a bit of both.
+ for (i = FIRST_COMMAND_TAG; i < LAST_COMMAND_TAG; i++) + { + for (j = i+1; j < LAST_COMMAND_TAG; j++) + { + int cmp = strcmp(tag_names[i], tag_names[j]); + if (cmp == 0) + { + elog(ERROR, "Found duplicate tag_name: \"%s\"", + tag_names[i]); + return false; + } + if (cmp > 0) + { + elog(ERROR, "Found commandnames out of order: \"%s\" before \"%s\"", + tag_names[i], tag_names[j]); + return false; + } + } + } + return true; +}And I think we could get rid of this with my earlier suggestions?
This is now handled by the perl script, also.
+/* + * BEWARE: These are in sorted order, but ordered by their printed + * values in the tag_name list (see common/commandtag.c). + * In particular it matters because the sort ordering changes + * when you replace a space with an underscore. To wit: + * + * "CREATE TABLE" + * "CREATE TABLE AS" + * "CREATE TABLESPACE" + * + * but... + * + * CREATE_TABLE + * CREATE_TABLESPACE + * CREATE_TABLE_AS + * + * It also matters that COMMANDTAG_UNKNOWN is written "???". + * + * If you add a value here, add it in common/commandtag.c also, and + * be careful to get the ordering right. You can build with + * COMMANDTAG_CHECKING to have this automatically checked + * at runtime, but that adds considerable overhead, so do so sparingly. + */ +typedef enum CommandTag +{This seems pretty darn nightmarish.
Well, it does get automatically checked for you.
+#define FIRST_COMMAND_TAG COMMANDTAG_UNKNOWN + COMMANDTAG_UNKNOWN, + COMMANDTAG_ALTER_ACCESS_METHOD, + COMMANDTAG_ALTER_AGGREGATE, + COMMANDTAG_ALTER_CAST, + COMMANDTAG_ALTER_COLLATION, + COMMANDTAG_ALTER_CONSTRAINT, + COMMANDTAG_ALTER_CONVERSION, + COMMANDTAG_ALTER_DATABASE, + COMMANDTAG_ALTER_DEFAULT_PRIVILEGES, + COMMANDTAG_ALTER_DOMAIN, [...]I'm a bit worried that this basically duplicates a good portion of NodeTag, without having otherwise much of a point?
There is not enough overlap between NodeTag and CommandTag for any obvious consolidation. Feel free to recommend something specific.
From a70b0cadc1142e92b2354a0ca3cd47aaeb0c148e Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Tue, 4 Feb 2020 12:25:05 -0800
Subject: [PATCH v2 2/3] Using a Bitmapset of tags rather than a string array.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bitEventTriggerCacheItem no longer holds an array of palloc’d tag strings
in sorted order, but rather just a Bitmapset over the CommandTags. This
makes the code a little simpler and easier to read, in my opinion. In
filter_event_trigger, rather than running bsearch through a sorted array
of strings, it just runs bms_is_member.
---It seems weird to add the bsearch just to remove it immediately again a
patch later. This probably should just go first?
I still don’t know what this comment means.
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql index 346168673d..cad02212ad 100644 --- a/src/test/regress/sql/event_trigger.sql +++ b/src/test/regress/sql/event_trigger.sql @@ -10,6 +10,13 @@ BEGIN END $$ language plpgsql;+-- OK +create function test_event_trigger2() returns event_trigger as $$ +BEGIN + RAISE NOTICE 'test_event_trigger2: % %', tg_event, tg_tag; +END +$$ LANGUAGE plpgsql; + -- should fail, event triggers cannot have declared arguments create function test_event_trigger_arg(name text) returns event_trigger as $$ BEGIN RETURN 1; END $$ language plpgsql; @@ -82,6 +89,783 @@ create event trigger regress_event_trigger2 on ddl_command_start -- OK comment on event trigger regress_event_trigger is 'test comment';+-- These are all unsupported +create event trigger regress_event_triger_NULL on ddl_command_start + when tag in ('') + execute procedure test_event_trigger2(); + +create event trigger regress_event_triger_UNKNOWN on ddl_command_start + when tag in ('???') + execute procedure test_event_trigger2(); + +create event trigger regress_event_trigger_ALTER_DATABASE on ddl_command_start + when tag in ('ALTER DATABASE') + execute procedure test_event_trigger2();[...]
There got to be a more maintainable way to write this.
This has all been removed from version 3 of the patch set.
Attachments:
v3-0001-Migrating-commandTag-from-string-to-enum.patchapplication/octet-stream; name=v3-0001-Migrating-commandTag-from-string-to-enum.patch; x-unix-mode=0644Download
From a97e83feb21be4e583ee82f136021645a33633d4 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Thu, 6 Feb 2020 11:41:44 -0800
Subject: [PATCH v3] Migrating commandTag from string to enum.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The backend was using strings to represent command tags and doing string
comparisons in multiple places. Fixing that by creating a new
CommandTag enum and using it instead.
Replacing numerous occurrences of char *completionTag with a
QueryCompletionData struct so that the code no longer stores information
about completed queries in a cstring. Only at the last moment, in
EndCommand(), does this get converted to a string.
EventTriggerCacheItem no longer holds an array of palloc’d tag strings
in sorted order, but rather just a Bitmapset over the CommandTags.
---
.../pg_stat_statements/pg_stat_statements.c | 18 +-
contrib/sepgsql/hooks.c | 6 +-
src/backend/commands/createas.c | 14 +-
src/backend/commands/event_trigger.c | 162 +---
src/backend/commands/matview.c | 2 +-
src/backend/commands/portalcmds.c | 16 +-
src/backend/commands/prepare.c | 4 +-
src/backend/executor/execMain.c | 4 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/spi.c | 22 +-
src/backend/replication/walsender.c | 18 +-
src/backend/tcop/dest.c | 37 +-
src/backend/tcop/postgres.c | 24 +-
src/backend/tcop/pquery.c | 111 +--
src/backend/tcop/utility.c | 575 ++++++-------
src/backend/utils/cache/evtcache.c | 27 +-
src/backend/utils/cache/plancache.c | 4 +-
src/backend/utils/misc/.gitignore | 1 +
src/backend/utils/misc/Makefile | 14 +
src/backend/utils/misc/commandtag.c | 775 ++++++++++++++++++
.../utils/misc/reconcile_commandtag_enum.pl | 215 +++++
src/backend/utils/mmgr/portalmem.c | 6 +-
src/include/commands/createas.h | 2 +-
src/include/commands/event_trigger.h | 2 +-
src/include/commands/matview.h | 2 +-
src/include/commands/portalcmds.h | 2 +-
src/include/commands/prepare.h | 2 +-
src/include/miscadmin.h | 4 +
src/include/nodes/parsenodes.h | 1 +
src/include/tcop/dest.h | 7 +-
src/include/tcop/pquery.h | 2 +-
src/include/tcop/utility.h | 15 +-
src/include/utils/commandtag.h | 297 +++++++
src/include/utils/evtcache.h | 3 +-
src/include/utils/plancache.h | 6 +-
src/include/utils/portal.h | 6 +-
src/include/utils/querycompletion.h | 46 ++
src/pl/plpgsql/src/pl_exec.c | 9 +-
.../test_ddl_deparse/test_ddl_deparse.c | 2 +-
39 files changed, 1865 insertions(+), 602 deletions(-)
create mode 100644 src/backend/utils/misc/commandtag.c
create mode 100755 src/backend/utils/misc/reconcile_commandtag_enum.pl
create mode 100644 src/include/utils/commandtag.h
create mode 100644 src/include/utils/querycompletion.h
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 6f82a671ee..cd1d98114e 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -307,7 +307,7 @@ static void pgss_ExecutorEnd(QueryDesc *queryDesc);
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
static uint64 pgss_hash_string(const char *str, int len);
static void pgss_store(const char *query, uint64 queryId,
int query_location, int query_len,
@@ -960,7 +960,7 @@ static void
pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context,
ParamListInfo params, QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletionData *qc)
{
Node *parsetree = pstmt->utilityStmt;
@@ -998,11 +998,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
@@ -1013,10 +1013,8 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
INSTR_TIME_SET_CURRENT(duration);
INSTR_TIME_SUBTRACT(duration, start);
- /* parse command tag to retrieve the number of affected rows. */
- if (completionTag &&
- strncmp(completionTag, "COPY ", 5) == 0)
- rows = pg_strtouint64(completionTag + 5, NULL, 10);
+ if (qc && qc->commandTag == COMMANDTAG_COPY)
+ rows = qc->nprocessed;
else
rows = 0;
@@ -1060,11 +1058,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
}
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 997a64c87e..999a67bd00 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -317,7 +317,7 @@ sepgsql_utility_command(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
Node *parsetree = pstmt->utilityStmt;
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
@@ -380,11 +380,11 @@ sepgsql_utility_command(PlannedStmt *pstmt,
if (next_ProcessUtility_hook)
(*next_ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cc02cf824e..a0eb1950ce 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -10,7 +10,7 @@
*
* Formerly, CTAS was implemented as a variant of SELECT, which led
* to assorted legacy behaviors that we still try to preserve, notably that
- * we must return a tuples-processed count in the completionTag. (We no
+ * we must return a tuples-processed count in the qcdata. (We no
* longer do that for CTAS ... WITH NO DATA, however.)
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
@@ -225,7 +225,7 @@ create_ctas_nodata(List *tlist, IntoClause *into)
ObjectAddress
ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ParamListInfo params, QueryEnvironment *queryEnv,
- char *completionTag)
+ QueryCompletionData *qc)
{
Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
@@ -270,7 +270,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
Assert(!is_matview); /* excluded by syntax */
- ExecuteQuery(pstate, estmt, into, params, dest, completionTag);
+ ExecuteQuery(pstate, estmt, into, params, dest, qc);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
@@ -352,11 +352,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* run the plan to completion */
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
- /* save the rowcount if we're given a completionTag to fill */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ /* save the rowcount if we're given a qc to fill */
+ if (qc)
+ SetQC(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 71911d4067..b7925aeff2 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -41,6 +41,7 @@
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/commandtag.h"
#include "utils/evtcache.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
@@ -78,59 +79,6 @@ typedef struct
bool supported;
} event_trigger_support_data;
-typedef enum
-{
- EVENT_TRIGGER_COMMAND_TAG_OK,
- EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED,
- EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED
-} event_trigger_command_tag_check_result;
-
-/* XXX merge this with ObjectTypeMap? */
-static const event_trigger_support_data event_trigger_support[] = {
- {"ACCESS METHOD", true},
- {"AGGREGATE", true},
- {"CAST", true},
- {"CONSTRAINT", true},
- {"COLLATION", true},
- {"CONVERSION", true},
- {"DATABASE", false},
- {"DOMAIN", true},
- {"EXTENSION", true},
- {"EVENT TRIGGER", false},
- {"FOREIGN DATA WRAPPER", true},
- {"FOREIGN TABLE", true},
- {"FUNCTION", true},
- {"INDEX", true},
- {"LANGUAGE", true},
- {"MATERIALIZED VIEW", true},
- {"OPERATOR", true},
- {"OPERATOR CLASS", true},
- {"OPERATOR FAMILY", true},
- {"POLICY", true},
- {"PROCEDURE", true},
- {"PUBLICATION", true},
- {"ROLE", false},
- {"ROUTINE", true},
- {"RULE", true},
- {"SCHEMA", true},
- {"SEQUENCE", true},
- {"SERVER", true},
- {"STATISTICS", true},
- {"SUBSCRIPTION", true},
- {"TABLE", true},
- {"TABLESPACE", false},
- {"TRANSFORM", true},
- {"TRIGGER", true},
- {"TEXT SEARCH CONFIGURATION", true},
- {"TEXT SEARCH DICTIONARY", true},
- {"TEXT SEARCH PARSER", true},
- {"TEXT SEARCH TEMPLATE", true},
- {"TYPE", true},
- {"USER MAPPING", true},
- {"VIEW", true},
- {NULL, false}
-};
-
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -150,8 +98,7 @@ typedef struct SQLDropObject
static void AlterEventTriggerOwner_internal(Relation rel,
HeapTuple tup,
Oid newOwnerId);
-static event_trigger_command_tag_check_result check_ddl_tag(const char *tag);
-static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(const char *tag);
+static bool command_tag_table_rewrite_ok(CommandTag commandTag);
static void error_duplicate_filter_variable(const char *defname);
static Datum filter_list_to_array(List *filterlist);
static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname,
@@ -259,71 +206,23 @@ validate_ddl_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
- event_trigger_command_tag_check_result result;
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
- result = check_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED)
+ if (commandTag == COMMANDTAG_UNKNOWN)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
- tag, filtervar)));
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
+ tagstr, filtervar)));
+ if ( ! command_tag_event_trigger_ok(commandTag))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
-static event_trigger_command_tag_check_result
-check_ddl_tag(const char *tag)
-{
- const char *obtypename;
- const event_trigger_support_data *etsd;
-
- /*
- * Handle some idiosyncratic special cases.
- */
- if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
- pg_strcasecmp(tag, "SELECT INTO") == 0 ||
- pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
- pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
- pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
- pg_strcasecmp(tag, "COMMENT") == 0 ||
- pg_strcasecmp(tag, "GRANT") == 0 ||
- pg_strcasecmp(tag, "REVOKE") == 0 ||
- pg_strcasecmp(tag, "DROP OWNED") == 0 ||
- pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
- /*
- * Otherwise, command should be CREATE, ALTER, or DROP.
- */
- if (pg_strncasecmp(tag, "CREATE ", 7) == 0)
- obtypename = tag + 7;
- else if (pg_strncasecmp(tag, "ALTER ", 6) == 0)
- obtypename = tag + 6;
- else if (pg_strncasecmp(tag, "DROP ", 5) == 0)
- obtypename = tag + 5;
- else
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
-
- /*
- * ...and the object type should be something recognizable.
- */
- for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++)
- if (pg_strcasecmp(etsd->obtypename, obtypename) == 0)
- break;
- if (etsd->obtypename == NULL)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
- if (!etsd->supported)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-}
-
/*
* Validate DDL command tags for event table_rewrite.
*/
@@ -334,29 +233,18 @@ validate_table_rewrite_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
- event_trigger_command_tag_check_result result;
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
- result = check_table_rewrite_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
+ if (! command_tag_table_rewrite_ok(commandTag))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
-static event_trigger_command_tag_check_result
-check_table_rewrite_ddl_tag(const char *tag)
-{
- if (pg_strcasecmp(tag, "ALTER TABLE") == 0 ||
- pg_strcasecmp(tag, "ALTER TYPE") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
-}
-
/*
* Complain about a duplicate filter variable.
*/
@@ -663,7 +551,7 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
* tags matching.
*/
static bool
-filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
+filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
{
/*
* Filter by session replication role, knowing that we never see disabled
@@ -681,9 +569,7 @@ filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
}
/* Filter by tags, if any were specified. */
- if (item->ntags != 0 && bsearch(tag, item->tag,
- item->ntags, sizeof(char *),
- pg_qsort_strcmp) == NULL)
+ if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
return false;
/* if we reach that point, we're not filtering out this item */
@@ -700,7 +586,7 @@ EventTriggerCommonSetup(Node *parsetree,
EventTriggerEvent event, const char *eventstr,
EventTriggerData *trigdata)
{
- const char *tag;
+ CommandTag tag;
List *cachelist;
ListCell *lc;
List *runlist = NIL;
@@ -716,25 +602,25 @@ EventTriggerCommonSetup(Node *parsetree,
*
* If this cross-check fails for you, you probably need to either adjust
* standard_ProcessUtility() not to invoke event triggers for the command
- * type in question, or you need to adjust check_ddl_tag to accept the
+ * type in question, or you need to adjust event_trigger_ok to accept the
* relevant command tag.
*/
#ifdef USE_ASSERT_CHECKING
{
- const char *dbgtag;
+ CommandTag dbgtag;
dbgtag = CreateCommandTag(parsetree);
if (event == EVT_DDLCommandStart ||
event == EVT_DDLCommandEnd ||
event == EVT_SQLDrop)
{
- if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ if (! command_tag_event_trigger_ok(dbgtag))
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
else if (event == EVT_TableRewrite)
{
- if (check_table_rewrite_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ if (! command_tag_table_rewrite_ok(dbgtag))
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
}
#endif
@@ -758,7 +644,7 @@ EventTriggerCommonSetup(Node *parsetree,
{
EventTriggerCacheItem *item = lfirst(lc);
- if (filter_event_trigger(&tag, item))
+ if (filter_event_trigger(tag, item))
{
/* We must plan to fire this trigger. */
runlist = lappend_oid(runlist, item->fnoid);
@@ -2136,7 +2022,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
values[i++] = Int32GetDatum(addr.objectSubId);
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
/* object_type */
values[i++] = CStringGetTextDatum(type);
/* schema */
@@ -2161,7 +2047,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
nulls[i++] = true;
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
/* object_type */
values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
/* schema */
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 1ee37c1aeb..45eafd4472 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -136,7 +136,7 @@ SetMatViewPopulatedState(Relation relation, bool newstate)
*/
ObjectAddress
ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag)
+ ParamListInfo params, QueryCompletionData *qc)
{
Oid matviewOid;
Relation matviewRel;
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 7e5c805a1e..b4c58c2410 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -106,7 +106,8 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
PortalDefineQuery(portal,
NULL,
queryString,
- "SELECT", /* cursor's query is always a SELECT */
+ COMMANDTAG_SELECT, /* cursor's query is always a
+ * SELECT */
list_make1(plan),
NULL);
@@ -160,15 +161,14 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
*
* stmt: parsetree node for command
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store a command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*/
void
PerformPortalFetch(FetchStmt *stmt,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
Portal portal;
uint64 nprocessed;
@@ -203,10 +203,8 @@ PerformPortalFetch(FetchStmt *stmt,
dest);
/* Return command status if wanted */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT,
- stmt->ismove ? "MOVE" : "FETCH",
- nprocessed);
+ if (qc)
+ SetQC(qc, stmt->ismove ? COMMANDTAG_MOVE : COMMANDTAG_FETCH, nprocessed);
}
/*
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index c4e4b6eaec..18f6227fde 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -187,7 +187,7 @@ void
ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletionData *qc)
{
PreparedStatement *entry;
CachedPlan *cplan;
@@ -288,7 +288,7 @@ ExecuteQuery(ParseState *pstate,
*/
PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
- (void) PortalRun(portal, count, false, true, dest, dest, completionTag);
+ (void) PortalRun(portal, count, false, true, dest, dest, qc);
PortalDrop(portal, false);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index ee5c3a60ff..751b36b6bf 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -787,11 +787,11 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
if (isTempNamespace(get_rel_namespace(rte->relid)))
continue;
- PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
+ PreventCommandTagIfReadOnly(CreateCommandTag((Node *) plannedstmt));
}
if (plannedstmt->commandType != CMD_SELECT || plannedstmt->hasModifyingCTE)
- PreventCommandIfParallelMode(CreateCommandTag((Node *) plannedstmt));
+ PreventCommandTagIfParallelMode(CreateCommandTag((Node *) plannedstmt));
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 5cff6c4321..9b45a8a9a0 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -530,7 +530,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a SQL function",
- CreateCommandTag(stmt->utilityStmt))));
+ CreateCommandName(stmt->utilityStmt))));
}
if (fcache->readonly_func && !CommandIsReadOnly(stmt))
@@ -538,7 +538,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ CreateCommandName((Node *) stmt))));
/* OK, build the execution_state for this query */
newes = (execution_state *) palloc(sizeof(execution_state));
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index c46764bf42..82daddb945 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1338,7 +1338,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
/* translator: %s is name of a SQL command, eg INSERT */
errmsg("cannot open %s query as cursor",
- plansource->commandTag)));
+ GetCommandTagName(plansource->commandTag))));
}
Assert(list_length(plan->plancache_list) == 1);
@@ -1469,7 +1469,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) pstmt))));
+ CreateCommandName((Node *) pstmt))));
}
}
@@ -2255,7 +2255,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ CreateCommandName((Node *) stmt))));
/*
* If not read-only mode, advance the command counter before each
@@ -2291,9 +2291,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else
{
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletionData qcdata;
ProcessUtilityContext context;
+ InitializeQC(&qcdata);
+
/*
* If the SPI context is atomic, or we are asked to manage
* snapshots, then we are in an atomic execution context.
@@ -2312,7 +2314,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
paramLI,
_SPI_current->queryEnv,
dest,
- completionTag);
+ &qcdata);
/* Update "processed" if stmt returned tuples */
if (_SPI_current->tuptable)
@@ -2328,9 +2330,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
{
CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
- if (strncmp(completionTag, "SELECT ", 7) == 0)
- _SPI_current->processed =
- pg_strtouint64(completionTag + 7, NULL, 10);
+ if (qcdata.commandTag == COMMANDTAG_SELECT)
+ _SPI_current->processed = qcdata.nprocessed;
else
{
/*
@@ -2351,9 +2352,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else if (IsA(stmt->utilityStmt, CopyStmt))
{
- Assert(strncmp(completionTag, "COPY ", 5) == 0);
- _SPI_current->processed = pg_strtouint64(completionTag + 5,
- NULL, 10);
+ Assert(qcdata.commandTag == COMMANDTAG_COPY);
+ _SPI_current->processed = qcdata.nprocessed;
}
}
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index abb533b9d0..c000d5b174 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1074,8 +1074,11 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
static void
DropReplicationSlot(DropReplicationSlotCmd *cmd)
{
+ QueryCompletionData qcdata;
+
ReplicationSlotDrop(cmd->slotname, !cmd->wait);
- EndCommand("DROP_REPLICATION_SLOT", DestRemote);
+ SetQC(&qcdata, COMMANDTAG_DROP_REPLICATION_SLOT, 0);
+ EndCommand(&qcdata, DestRemote, false);
}
/*
@@ -1086,6 +1089,7 @@ static void
StartLogicalReplication(StartReplicationCmd *cmd)
{
StringInfoData buf;
+ QueryCompletionData qcdata;
/* make sure that our requirements are still fulfilled */
CheckLogicalDecodingRequirements();
@@ -1160,7 +1164,8 @@ StartLogicalReplication(StartReplicationCmd *cmd)
WalSndSetState(WALSNDSTATE_STARTUP);
/* Get out of COPY mode (CommandComplete). */
- EndCommand("COPY 0", DestRemote);
+ SetQC(&qcdata, COMMANDTAG_COPY, 0);
+ EndCommand(&qcdata, DestRemote, false);
}
/*
@@ -1464,6 +1469,7 @@ exec_replication_command(const char *cmd_string)
Node *cmd_node;
MemoryContext cmd_context;
MemoryContext old_context;
+ QueryCompletionData qcdata;
/*
* If WAL sender has been told that shutdown is getting close, switch its
@@ -1614,7 +1620,8 @@ exec_replication_command(const char *cmd_string)
MemoryContextDelete(cmd_context);
/* Send CommandComplete message */
- EndCommand("SELECT", DestRemote);
+ SetQC(&qcdata, COMMANDTAG_SELECT, 0);
+ EndCommand(&qcdata, DestRemote, true);
/* Report to pgstat that this process is now idle */
pgstat_report_activity(STATE_IDLE, NULL);
@@ -2867,8 +2874,11 @@ WalSndDone(WalSndSendDataCallback send_data)
if (WalSndCaughtUp && sentPtr == replicatedPtr &&
!pq_is_send_pending())
{
+ QueryCompletionData qcdata;
+
/* Inform the standby that XLOG streaming is done */
- EndCommand("COPY 0", DestRemote);
+ SetQC(&qcdata, COMMANDTAG_COPY, 0);
+ EndCommand(&qcdata, DestRemote, false);
pq_flush();
proc_exit(0);
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 09c1dcbb53..c526b9e346 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -100,7 +100,7 @@ DestReceiver *None_Receiver = (DestReceiver *) &donothingDR;
* ----------------
*/
void
-BeginCommand(const char *commandTag, CommandDest dest)
+BeginCommand(CommandTag commandTag, CommandDest dest)
{
/* Nothing to do at present */
}
@@ -163,8 +163,12 @@ CreateDestReceiver(CommandDest dest)
* ----------------
*/
void
-EndCommand(const char *commandTag, CommandDest dest)
+EndCommand(const QueryCompletionData * qc, CommandDest dest, bool force_undecorated_output)
{
+ char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag tag;
+ const char *tagname;
+
switch (dest)
{
case DestRemote:
@@ -172,11 +176,32 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestRemoteSimple:
/*
- * We assume the commandTag is plain ASCII and therefore requires
- * no encoding conversion.
+ * We assume the tagname is plain ASCII and therefore requires no
+ * encoding conversion.
+ *
+ * We no longer display LastOid, but to preserve the wire protocol,
+ * we write InvalidOid where the LastOid used to be written. For
+ * efficiency in the snprintf(), hard-code InvalidOid as zero.
+ *
+ * All cases where LastOid was written also write nprocessed count,
+ * so just Assert that rather than having an extra test.
*/
- pq_putmessage('C', commandTag, strlen(commandTag) + 1);
- break;
+ tag = qc->commandTag;
+ tagname = GetCommandTagName(tag);
+
+ if (command_tag_display_last_oid(tag) && !force_undecorated_output)
+ {
+ Assert(InvalidOid == 0);
+ Assert(command_tag_display_rowcount(tag));
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "%s 0 " UINT64_FORMAT, tagname, qc->nprocessed);
+ }
+ else if (command_tag_display_rowcount(tag) && !force_undecorated_output)
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "%s " UINT64_FORMAT, tagname, qc->nprocessed);
+ else
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s", tagname);
+ pq_putmessage('C', completionTag, strlen(completionTag) + 1);
case DestNone:
case DestDebug:
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0a6f80963b..6f90c996f5 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1064,8 +1064,8 @@ exec_simple_query(const char *query_string)
{
RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);
bool snapshot_set = false;
- const char *commandTag;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag commandTag;
+ QueryCompletionData qcdata;
MemoryContext per_parsetree_context = NULL;
List *querytree_list,
*plantree_list;
@@ -1081,7 +1081,7 @@ exec_simple_query(const char *query_string)
*/
commandTag = CreateCommandTag(parsetree->stmt);
- set_ps_display(commandTag, false);
+ set_ps_display(GetCommandTagName(commandTag), false);
BeginCommand(commandTag, dest);
@@ -1230,7 +1230,7 @@ exec_simple_query(const char *query_string)
true,
receiver,
receiver,
- completionTag);
+ &qcdata);
receiver->rDestroy(receiver);
@@ -1281,7 +1281,7 @@ exec_simple_query(const char *query_string)
* command the client sent, regardless of rewriting. (But a command
* aborted by error will not send an EndCommand report at all.)
*/
- EndCommand(completionTag, dest);
+ EndCommand(&qcdata, dest, false);
/* Now we may drop the per-parsetree context, if one was created. */
if (per_parsetree_context)
@@ -1343,7 +1343,7 @@ exec_parse_message(const char *query_string, /* string to execute */
MemoryContext oldcontext;
List *parsetree_list;
RawStmt *raw_parse_tree;
- const char *commandTag;
+ CommandTag commandTag;
List *querytree_list;
CachedPlanSource *psrc;
bool is_named;
@@ -1505,7 +1505,7 @@ exec_parse_message(const char *query_string, /* string to execute */
{
/* Empty input string. This is legal. */
raw_parse_tree = NULL;
- commandTag = NULL;
+ commandTag = COMMANDTAG_UNKNOWN;
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
querytree_list = NIL;
}
@@ -2022,7 +2022,7 @@ exec_execute_message(const char *portal_name, long max_rows)
DestReceiver *receiver;
Portal portal;
bool completed;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletionData qcdata;
const char *sourceText;
const char *prepStmtName;
ParamListInfo portalParams;
@@ -2049,7 +2049,7 @@ exec_execute_message(const char *portal_name, long max_rows)
* If the original query was a null string, just return
* EmptyQueryResponse.
*/
- if (portal->commandTag == NULL)
+ if (portal->commandTag == COMMANDTAG_UNKNOWN)
{
Assert(portal->stmts == NIL);
NullCommand(dest);
@@ -2095,7 +2095,7 @@ exec_execute_message(const char *portal_name, long max_rows)
pgstat_report_activity(STATE_RUNNING, sourceText);
- set_ps_display(portal->commandTag, false);
+ set_ps_display(GetCommandTagName(portal->commandTag), false);
if (save_log_statement_stats)
ResetUsage();
@@ -2176,7 +2176,7 @@ exec_execute_message(const char *portal_name, long max_rows)
!execute_is_fetch && max_rows == FETCH_ALL,
receiver,
receiver,
- completionTag);
+ &qcdata);
receiver->rDestroy(receiver);
@@ -2209,7 +2209,7 @@ exec_execute_message(const char *portal_name, long max_rows)
}
/* Send appropriate CommandComplete to client */
- EndCommand(completionTag, dest);
+ EndCommand(&qcdata, dest, false);
}
else
{
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 0f5801e046..8a198aabd6 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -40,7 +40,7 @@ static void ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletionData *qc);
static void FillPortalStore(Portal portal, bool isTopLevel);
static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
DestReceiver *dest);
@@ -48,11 +48,11 @@ static uint64 PortalRunSelect(Portal portal, bool forward, long count,
DestReceiver *dest);
static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
static void PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletionData *qc);
static uint64 DoPortalRunFetch(Portal portal,
FetchDirection fdirection,
long count,
@@ -125,10 +125,9 @@ FreeQueryDesc(QueryDesc *qdesc)
* sourceText: the source text of the query
* params: any parameters needed
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store the command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want a status string.
*
* Must be called in a memory context that will be reset or deleted on
* error; otherwise the executor's memory usage will be leaked.
@@ -139,7 +138,7 @@ ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
QueryDesc *queryDesc;
@@ -161,38 +160,26 @@ ProcessQuery(PlannedStmt *plan,
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
/*
- * Build command completion status string, if caller wants one.
+ * Build command completion status data, if caller wants one.
*/
- if (completionTag)
+ if (qc)
{
- Oid lastOid;
-
switch (queryDesc->operation)
{
case CMD_SELECT:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQC(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed);
break;
case CMD_INSERT:
- /* lastoid doesn't exist anymore */
- lastOid = InvalidOid;
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "INSERT %u " UINT64_FORMAT,
- lastOid, queryDesc->estate->es_processed);
+ SetQC(qc, COMMANDTAG_INSERT, queryDesc->estate->es_processed);
break;
case CMD_UPDATE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "UPDATE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQC(qc, COMMANDTAG_UPDATE, queryDesc->estate->es_processed);
break;
case CMD_DELETE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "DELETE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQC(qc, COMMANDTAG_DELETE, queryDesc->estate->es_processed);
break;
default:
- strcpy(completionTag, "???");
+ SetQC(qc, COMMANDTAG_UNKNOWN, queryDesc->estate->es_processed);
break;
}
}
@@ -675,9 +662,8 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
*
* altdest: where to send output of non-primary queries
*
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
- * May be NULL if caller doesn't want a status string.
+ * qc: where to store command completion status data.
+ * May be NULL if caller doesn't want status data.
*
* Returns true if the portal's execution is complete, false if it was
* suspended due to exhaustion of the count parameter.
@@ -685,7 +671,7 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
bool
PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletionData *qc)
{
bool result;
uint64 nprocessed;
@@ -700,9 +686,9 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
TRACE_POSTGRESQL_QUERY_EXECUTE_START();
- /* Initialize completion tag to empty string */
- if (completionTag)
- completionTag[0] = '\0';
+ /* Initialize empty completion data */
+ if (qc)
+ InitializeQC(qc);
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
{
@@ -771,16 +757,12 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
/*
* If the portal result contains a command tag and the caller
- * gave us a pointer to store it, copy it. Patch the "SELECT"
- * tag to also provide the rowcount.
+ * gave us a pointer to store it, copy it and update the rowcount.
*/
- if (completionTag && portal->commandTag)
+ if (qc && portal->qcdata.commandTag != COMMANDTAG_UNKNOWN)
{
- if (strcmp(portal->commandTag, "SELECT") == 0)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT, nprocessed);
- else
- strcpy(completionTag, portal->commandTag);
+ CopyQC(qc, &portal->qcdata);
+ qc->nprocessed = nprocessed;
}
/* Mark portal not active */
@@ -794,7 +776,7 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
case PORTAL_MULTI_QUERY:
PortalRunMulti(portal, isTopLevel, false,
- dest, altdest, completionTag);
+ dest, altdest, qc);
/* Prevent portal's commands from being re-executed */
MarkPortalDone(portal);
@@ -1005,8 +987,9 @@ static void
FillPortalStore(Portal portal, bool isTopLevel)
{
DestReceiver *treceiver;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletionData qcdata;
+ InitializeQC(&qcdata);
PortalCreateHoldStore(portal);
treceiver = CreateDestReceiver(DestTuplestore);
SetTuplestoreDestReceiverParams(treceiver,
@@ -1014,8 +997,6 @@ FillPortalStore(Portal portal, bool isTopLevel)
portal->holdContext,
false);
- completionTag[0] = '\0';
-
switch (portal->strategy)
{
case PORTAL_ONE_RETURNING:
@@ -1028,12 +1009,12 @@ FillPortalStore(Portal portal, bool isTopLevel)
* portal's holdSnapshot to the snapshot used (or a copy of it).
*/
PortalRunMulti(portal, isTopLevel, true,
- treceiver, None_Receiver, completionTag);
+ treceiver, None_Receiver, &qcdata);
break;
case PORTAL_UTIL_SELECT:
PortalRunUtility(portal, linitial_node(PlannedStmt, portal->stmts),
- isTopLevel, true, treceiver, completionTag);
+ isTopLevel, true, treceiver, &qcdata);
break;
default:
@@ -1042,9 +1023,9 @@ FillPortalStore(Portal portal, bool isTopLevel)
break;
}
- /* Override default completion tag with actual command result */
- if (completionTag[0] != '\0')
- portal->commandTag = pstrdup(completionTag);
+ /* Override portal completion data with actual command results */
+ if (qcdata.commandTag != COMMANDTAG_UNKNOWN)
+ CopyQC(&portal->qcdata, &qcdata);
treceiver->rDestroy(treceiver);
}
@@ -1130,7 +1111,7 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count,
static void
PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletionData *qc)
{
Node *utilityStmt = pstmt->utilityStmt;
Snapshot snapshot;
@@ -1178,7 +1159,7 @@ PortalRunUtility(Portal portal, PlannedStmt *pstmt,
portal->portalParams,
portal->queryEnv,
dest,
- completionTag);
+ qc);
/* Some utility statements may change context on us */
MemoryContextSwitchTo(portal->portalContext);
@@ -1202,7 +1183,7 @@ static void
PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletionData *qc)
{
bool active_snapshot_set = false;
ListCell *stmtlist_item;
@@ -1284,7 +1265,7 @@ PortalRunMulti(Portal portal,
portal->sourceText,
portal->portalParams,
portal->queryEnv,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1319,7 +1300,7 @@ PortalRunMulti(Portal portal,
Assert(!active_snapshot_set);
/* statement can set tag string */
PortalRunUtility(portal, pstmt, isTopLevel, false,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1350,8 +1331,8 @@ PortalRunMulti(Portal portal,
PopActiveSnapshot();
/*
- * If a command completion tag was supplied, use it. Otherwise use the
- * portal's commandTag as the default completion tag.
+ * If a query completion data was supplied, use it. Otherwise use the
+ * portal's query completion data.
*
* Exception: Clients expect INSERT/UPDATE/DELETE tags to have counts, so
* fake them with zeros. This can happen with DO INSTEAD rules if there
@@ -1361,18 +1342,12 @@ PortalRunMulti(Portal portal,
* e.g. an INSERT that does an UPDATE instead should not print "0 1" if
* one row was updated. See QueryRewrite(), step 3, for details.
*/
- if (completionTag && completionTag[0] == '\0')
+ if (qc && qc->commandTag == COMMANDTAG_UNKNOWN)
{
- if (portal->commandTag)
- strcpy(completionTag, portal->commandTag);
- if (strcmp(completionTag, "SELECT") == 0)
- sprintf(completionTag, "SELECT 0 0");
- else if (strcmp(completionTag, "INSERT") == 0)
- strcpy(completionTag, "INSERT 0 0");
- else if (strcmp(completionTag, "UPDATE") == 0)
- strcpy(completionTag, "UPDATE 0");
- else if (strcmp(completionTag, "DELETE") == 0)
- strcpy(completionTag, "DELETE 0");
+ if (portal->qcdata.commandTag != COMMANDTAG_UNKNOWN)
+ CopyQC(qc, &portal->qcdata);
+ /* If the caller supplied a qc, we should have set it by now. */
+ Assert(qc->commandTag != COMMANDTAG_UNKNOWN);
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index bb85b5e52a..5acdb084f3 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -83,10 +83,9 @@ static void ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletionData *qc);
static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
-
/*
* CommandIsReadOnly: is an executable query read-only?
*
@@ -412,6 +411,12 @@ PreventCommandIfReadOnly(const char *cmdname)
cmdname)));
}
+void
+PreventCommandTagIfReadOnly(CommandTag commandTag)
+{
+ PreventCommandIfReadOnly(GetCommandTagName(commandTag));
+}
+
/*
* PreventCommandIfParallelMode: throw error if current (sub)transaction is
* in parallel mode.
@@ -430,6 +435,12 @@ PreventCommandIfParallelMode(const char *cmdname)
cmdname)));
}
+void
+PreventCommandTagIfParallelMode(CommandTag commandTag)
+{
+ PreventCommandIfParallelMode(GetCommandTagName(commandTag));
+}
+
/*
* PreventCommandDuringRecovery: throw error if RecoveryInProgress
*
@@ -449,6 +460,12 @@ PreventCommandDuringRecovery(const char *cmdname)
cmdname)));
}
+void
+PreventCommandTagDuringRecovery(CommandTag commandTag)
+{
+ PreventCommandDuringRecovery(GetCommandTagName(commandTag));
+}
+
/*
* CheckRestrictedOperation: throw error for hazardous command if we're
* inside a security restriction context.
@@ -480,16 +497,13 @@ CheckRestrictedOperation(const char *cmdname)
* queryEnv: environment for parse through execution (e.g., ephemeral named
* tables like trigger transition tables). May be NULL.
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store command completion status data.
*
* Caller MUST supply a queryString; it is not allowed (anymore) to pass NULL.
* If you really don't have source text, you can pass a constant string,
* perhaps "(query not available)".
*
- * completionTag is only set nonempty if we want to return a nondefault status.
- *
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*
* Note for users of ProcessUtility_hook: the same queryString may be passed
* to multiple invocations of ProcessUtility when processing a query string
@@ -507,7 +521,7 @@ ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
Assert(IsA(pstmt, PlannedStmt));
Assert(pstmt->commandType == CMD_UTILITY);
@@ -521,11 +535,11 @@ ProcessUtility(PlannedStmt *pstmt,
if (ProcessUtility_hook)
(*ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
/*
@@ -546,7 +560,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -562,18 +576,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (readonly_flags != COMMAND_IS_STRICTLY_READ_ONLY &&
(XactReadOnly || IsInParallelMode()))
{
- const char *commandtag = CreateCommandTag(parsetree);
+ CommandTag commandtag = CreateCommandTag(parsetree);
if ((readonly_flags & COMMAND_OK_IN_READ_ONLY_TXN) == 0)
- PreventCommandIfReadOnly(commandtag);
+ PreventCommandTagIfReadOnly(commandtag);
if ((readonly_flags & COMMAND_OK_IN_PARALLEL_MODE) == 0)
- PreventCommandIfParallelMode(commandtag);
+ PreventCommandTagIfParallelMode(commandtag);
if ((readonly_flags & COMMAND_OK_IN_RECOVERY) == 0)
- PreventCommandDuringRecovery(commandtag);
+ PreventCommandTagDuringRecovery(commandtag);
}
- if (completionTag)
- completionTag[0] = '\0';
+ if (qc)
+ InitializeQC(qc);
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -623,18 +637,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case TRANS_STMT_COMMIT:
if (!EndTransactionBlock(stmt->chain))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQC(qc, COMMANDTAG_ROLLBACK, 0);
}
break;
case TRANS_STMT_PREPARE:
if (!PrepareTransactionBlock(stmt->gid))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQC(qc, COMMANDTAG_ROLLBACK, 0);
}
break;
@@ -693,8 +707,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case T_FetchStmt:
- PerformPortalFetch((FetchStmt *) parsetree, dest,
- completionTag);
+ PerformPortalFetch((FetchStmt *) parsetree, dest, qc);
break;
case T_DoStmt:
@@ -729,9 +742,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
DoCopy(pstate, (CopyStmt *) parsetree,
pstmt->stmt_location, pstmt->stmt_len,
&processed);
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "COPY " UINT64_FORMAT, processed);
+ if (qc)
+ SetQC(qc, COMMANDTAG_COPY, processed);
}
break;
@@ -745,7 +757,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ExecuteQuery(pstate,
(ExecuteStmt *) parsetree, NULL,
params,
- dest, completionTag);
+ dest, qc);
break;
case T_DeallocateStmt:
@@ -974,7 +986,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecuteGrantStmt(stmt);
}
@@ -987,7 +999,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecDropStmt(stmt, isTopLevel);
}
@@ -1000,7 +1012,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecRenameStmt(stmt);
}
@@ -1013,7 +1025,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectDependsStmt(stmt, NULL);
}
@@ -1026,7 +1038,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
}
@@ -1039,7 +1051,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterOwnerStmt(stmt);
}
@@ -1052,7 +1064,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
CommentObject(stmt);
break;
@@ -1065,7 +1077,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecSecLabelStmt(stmt);
break;
@@ -1075,7 +1087,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
break;
}
@@ -1102,7 +1114,7 @@ ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletionData *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -1605,7 +1617,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateTableAsStmt:
address = ExecCreateTableAs(pstate, (CreateTableAsStmt *) parsetree,
- params, queryEnv, completionTag);
+ params, queryEnv, qc);
break;
case T_RefreshMatViewStmt:
@@ -1620,7 +1632,7 @@ ProcessUtilitySlow(ParseState *pstate,
PG_TRY();
{
address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
- queryString, params, completionTag);
+ queryString, params, qc);
}
PG_FINALLY();
{
@@ -2099,137 +2111,137 @@ UtilityContainsQuery(Node *parsetree)
*
* This covers most cases where ALTER is used with an ObjectType enum.
*/
-static const char *
+static CommandTag
AlterObjectTypeCommandTag(ObjectType objtype)
{
- const char *tag;
+ CommandTag tag;
switch (objtype)
{
case OBJECT_AGGREGATE:
- tag = "ALTER AGGREGATE";
+ tag = COMMANDTAG_ALTER_AGGREGATE;
break;
case OBJECT_ATTRIBUTE:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case OBJECT_CAST:
- tag = "ALTER CAST";
+ tag = COMMANDTAG_ALTER_CAST;
break;
case OBJECT_COLLATION:
- tag = "ALTER COLLATION";
+ tag = COMMANDTAG_ALTER_COLLATION;
break;
case OBJECT_COLUMN:
- tag = "ALTER TABLE";
+ tag = COMMANDTAG_ALTER_TABLE;
break;
case OBJECT_CONVERSION:
- tag = "ALTER CONVERSION";
+ tag = COMMANDTAG_ALTER_CONVERSION;
break;
case OBJECT_DATABASE:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case OBJECT_DOMAIN:
case OBJECT_DOMCONSTRAINT:
- tag = "ALTER DOMAIN";
+ tag = COMMANDTAG_ALTER_DOMAIN;
break;
case OBJECT_EXTENSION:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case OBJECT_FDW:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "ALTER SERVER";
+ tag = COMMANDTAG_ALTER_SERVER;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "ALTER FOREIGN TABLE";
+ tag = COMMANDTAG_ALTER_FOREIGN_TABLE;
break;
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = COMMANDTAG_ALTER_FUNCTION;
break;
case OBJECT_INDEX:
- tag = "ALTER INDEX";
+ tag = COMMANDTAG_ALTER_INDEX;
break;
case OBJECT_LANGUAGE:
- tag = "ALTER LANGUAGE";
+ tag = COMMANDTAG_ALTER_LANGUAGE;
break;
case OBJECT_LARGEOBJECT:
- tag = "ALTER LARGE OBJECT";
+ tag = COMMANDTAG_ALTER_LARGE_OBJECT;
break;
case OBJECT_OPCLASS:
- tag = "ALTER OPERATOR CLASS";
+ tag = COMMANDTAG_ALTER_OPERATOR_CLASS;
break;
case OBJECT_OPERATOR:
- tag = "ALTER OPERATOR";
+ tag = COMMANDTAG_ALTER_OPERATOR;
break;
case OBJECT_OPFAMILY:
- tag = "ALTER OPERATOR FAMILY";
+ tag = COMMANDTAG_ALTER_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "ALTER POLICY";
+ tag = COMMANDTAG_ALTER_POLICY;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = COMMANDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROLE:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = COMMANDTAG_ALTER_ROUTINE;
break;
case OBJECT_RULE:
- tag = "ALTER RULE";
+ tag = COMMANDTAG_ALTER_RULE;
break;
case OBJECT_SCHEMA:
- tag = "ALTER SCHEMA";
+ tag = COMMANDTAG_ALTER_SCHEMA;
break;
case OBJECT_SEQUENCE:
- tag = "ALTER SEQUENCE";
+ tag = COMMANDTAG_ALTER_SEQUENCE;
break;
case OBJECT_TABLE:
case OBJECT_TABCONSTRAINT:
- tag = "ALTER TABLE";
+ tag = COMMANDTAG_ALTER_TABLE;
break;
case OBJECT_TABLESPACE:
- tag = "ALTER TABLESPACE";
+ tag = COMMANDTAG_ALTER_TABLESPACE;
break;
case OBJECT_TRIGGER:
- tag = "ALTER TRIGGER";
+ tag = COMMANDTAG_ALTER_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "ALTER EVENT TRIGGER";
+ tag = COMMANDTAG_ALTER_EVENT_TRIGGER;
break;
case OBJECT_TSCONFIGURATION:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_TSDICTIONARY:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSPARSER:
- tag = "ALTER TEXT SEARCH PARSER";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSTEMPLATE:
- tag = "ALTER TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TYPE:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case OBJECT_VIEW:
- tag = "ALTER VIEW";
+ tag = COMMANDTAG_ALTER_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "ALTER MATERIALIZED VIEW";
+ tag = COMMANDTAG_ALTER_MATERIALIZED_VIEW;
break;
case OBJECT_PUBLICATION:
- tag = "ALTER PUBLICATION";
+ tag = COMMANDTAG_ALTER_PUBLICATION;
break;
case OBJECT_SUBSCRIPTION:
- tag = "ALTER SUBSCRIPTION";
+ tag = COMMANDTAG_ALTER_SUBSCRIPTION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "ALTER STATISTICS";
+ tag = COMMANDTAG_ALTER_STATISTICS;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
@@ -2238,20 +2250,17 @@ AlterObjectTypeCommandTag(ObjectType objtype)
/*
* CreateCommandTag
- * utility to get a string representation of the command operation,
+ * utility to get a CommandTag for the command operation,
* given either a raw (un-analyzed) parsetree, an analyzed Query,
* or a PlannedStmt.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
- *
- * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
- * Also, the result must point at a true constant (permanent storage).
*/
-const char *
+CommandTag
CreateCommandTag(Node *parsetree)
{
- const char *tag;
+ CommandTag tag;
switch (nodeTag(parsetree))
{
@@ -2262,19 +2271,19 @@ CreateCommandTag(Node *parsetree)
/* raw plannable queries */
case T_InsertStmt:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case T_DeleteStmt:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case T_UpdateStmt:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case T_SelectStmt:
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
/* utility statements --- same whether raw or cooked */
@@ -2285,51 +2294,51 @@ CreateCommandTag(Node *parsetree)
switch (stmt->kind)
{
case TRANS_STMT_BEGIN:
- tag = "BEGIN";
+ tag = COMMANDTAG_BEGIN;
break;
case TRANS_STMT_START:
- tag = "START TRANSACTION";
+ tag = COMMANDTAG_START_TRANSACTION;
break;
case TRANS_STMT_COMMIT:
- tag = "COMMIT";
+ tag = COMMANDTAG_COMMIT;
break;
case TRANS_STMT_ROLLBACK:
case TRANS_STMT_ROLLBACK_TO:
- tag = "ROLLBACK";
+ tag = COMMANDTAG_ROLLBACK;
break;
case TRANS_STMT_SAVEPOINT:
- tag = "SAVEPOINT";
+ tag = COMMANDTAG_SAVEPOINT;
break;
case TRANS_STMT_RELEASE:
- tag = "RELEASE";
+ tag = COMMANDTAG_RELEASE;
break;
case TRANS_STMT_PREPARE:
- tag = "PREPARE TRANSACTION";
+ tag = COMMANDTAG_PREPARE_TRANSACTION;
break;
case TRANS_STMT_COMMIT_PREPARED:
- tag = "COMMIT PREPARED";
+ tag = COMMANDTAG_COMMIT_PREPARED;
break;
case TRANS_STMT_ROLLBACK_PREPARED:
- tag = "ROLLBACK PREPARED";
+ tag = COMMANDTAG_ROLLBACK_PREPARED;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
break;
case T_DeclareCursorStmt:
- tag = "DECLARE CURSOR";
+ tag = COMMANDTAG_DECLARE_CURSOR;
break;
case T_ClosePortalStmt:
@@ -2337,9 +2346,9 @@ CreateCommandTag(Node *parsetree)
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
if (stmt->portalname == NULL)
- tag = "CLOSE CURSOR ALL";
+ tag = COMMANDTAG_CLOSE_CURSOR_ALL;
else
- tag = "CLOSE CURSOR";
+ tag = COMMANDTAG_CLOSE_CURSOR;
}
break;
@@ -2347,209 +2356,209 @@ CreateCommandTag(Node *parsetree)
{
FetchStmt *stmt = (FetchStmt *) parsetree;
- tag = (stmt->ismove) ? "MOVE" : "FETCH";
+ tag = (stmt->ismove) ? COMMANDTAG_MOVE : COMMANDTAG_FETCH;
}
break;
case T_CreateDomainStmt:
- tag = "CREATE DOMAIN";
+ tag = COMMANDTAG_CREATE_DOMAIN;
break;
case T_CreateSchemaStmt:
- tag = "CREATE SCHEMA";
+ tag = COMMANDTAG_CREATE_SCHEMA;
break;
case T_CreateStmt:
- tag = "CREATE TABLE";
+ tag = COMMANDTAG_CREATE_TABLE;
break;
case T_CreateTableSpaceStmt:
- tag = "CREATE TABLESPACE";
+ tag = COMMANDTAG_CREATE_TABLESPACE;
break;
case T_DropTableSpaceStmt:
- tag = "DROP TABLESPACE";
+ tag = COMMANDTAG_DROP_TABLESPACE;
break;
case T_AlterTableSpaceOptionsStmt:
- tag = "ALTER TABLESPACE";
+ tag = COMMANDTAG_ALTER_TABLESPACE;
break;
case T_CreateExtensionStmt:
- tag = "CREATE EXTENSION";
+ tag = COMMANDTAG_CREATE_EXTENSION;
break;
case T_AlterExtensionStmt:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case T_AlterExtensionContentsStmt:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case T_CreateFdwStmt:
- tag = "CREATE FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER;
break;
case T_AlterFdwStmt:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case T_CreateForeignServerStmt:
- tag = "CREATE SERVER";
+ tag = COMMANDTAG_CREATE_SERVER;
break;
case T_AlterForeignServerStmt:
- tag = "ALTER SERVER";
+ tag = COMMANDTAG_ALTER_SERVER;
break;
case T_CreateUserMappingStmt:
- tag = "CREATE USER MAPPING";
+ tag = COMMANDTAG_CREATE_USER_MAPPING;
break;
case T_AlterUserMappingStmt:
- tag = "ALTER USER MAPPING";
+ tag = COMMANDTAG_ALTER_USER_MAPPING;
break;
case T_DropUserMappingStmt:
- tag = "DROP USER MAPPING";
+ tag = COMMANDTAG_DROP_USER_MAPPING;
break;
case T_CreateForeignTableStmt:
- tag = "CREATE FOREIGN TABLE";
+ tag = COMMANDTAG_CREATE_FOREIGN_TABLE;
break;
case T_ImportForeignSchemaStmt:
- tag = "IMPORT FOREIGN SCHEMA";
+ tag = COMMANDTAG_IMPORT_FOREIGN_SCHEMA;
break;
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_TABLE:
- tag = "DROP TABLE";
+ tag = COMMANDTAG_DROP_TABLE;
break;
case OBJECT_SEQUENCE:
- tag = "DROP SEQUENCE";
+ tag = COMMANDTAG_DROP_SEQUENCE;
break;
case OBJECT_VIEW:
- tag = "DROP VIEW";
+ tag = COMMANDTAG_DROP_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "DROP MATERIALIZED VIEW";
+ tag = COMMANDTAG_DROP_MATERIALIZED_VIEW;
break;
case OBJECT_INDEX:
- tag = "DROP INDEX";
+ tag = COMMANDTAG_DROP_INDEX;
break;
case OBJECT_TYPE:
- tag = "DROP TYPE";
+ tag = COMMANDTAG_DROP_TYPE;
break;
case OBJECT_DOMAIN:
- tag = "DROP DOMAIN";
+ tag = COMMANDTAG_DROP_DOMAIN;
break;
case OBJECT_COLLATION:
- tag = "DROP COLLATION";
+ tag = COMMANDTAG_DROP_COLLATION;
break;
case OBJECT_CONVERSION:
- tag = "DROP CONVERSION";
+ tag = COMMANDTAG_DROP_CONVERSION;
break;
case OBJECT_SCHEMA:
- tag = "DROP SCHEMA";
+ tag = COMMANDTAG_DROP_SCHEMA;
break;
case OBJECT_TSPARSER:
- tag = "DROP TEXT SEARCH PARSER";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "DROP TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "DROP TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "DROP TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "DROP FOREIGN TABLE";
+ tag = COMMANDTAG_DROP_FOREIGN_TABLE;
break;
case OBJECT_EXTENSION:
- tag = "DROP EXTENSION";
+ tag = COMMANDTAG_DROP_EXTENSION;
break;
case OBJECT_FUNCTION:
- tag = "DROP FUNCTION";
+ tag = COMMANDTAG_DROP_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "DROP PROCEDURE";
+ tag = COMMANDTAG_DROP_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "DROP ROUTINE";
+ tag = COMMANDTAG_DROP_ROUTINE;
break;
case OBJECT_AGGREGATE:
- tag = "DROP AGGREGATE";
+ tag = COMMANDTAG_DROP_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "DROP OPERATOR";
+ tag = COMMANDTAG_DROP_OPERATOR;
break;
case OBJECT_LANGUAGE:
- tag = "DROP LANGUAGE";
+ tag = COMMANDTAG_DROP_LANGUAGE;
break;
case OBJECT_CAST:
- tag = "DROP CAST";
+ tag = COMMANDTAG_DROP_CAST;
break;
case OBJECT_TRIGGER:
- tag = "DROP TRIGGER";
+ tag = COMMANDTAG_DROP_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "DROP EVENT TRIGGER";
+ tag = COMMANDTAG_DROP_EVENT_TRIGGER;
break;
case OBJECT_RULE:
- tag = "DROP RULE";
+ tag = COMMANDTAG_DROP_RULE;
break;
case OBJECT_FDW:
- tag = "DROP FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "DROP SERVER";
+ tag = COMMANDTAG_DROP_SERVER;
break;
case OBJECT_OPCLASS:
- tag = "DROP OPERATOR CLASS";
+ tag = COMMANDTAG_DROP_OPERATOR_CLASS;
break;
case OBJECT_OPFAMILY:
- tag = "DROP OPERATOR FAMILY";
+ tag = COMMANDTAG_DROP_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "DROP POLICY";
+ tag = COMMANDTAG_DROP_POLICY;
break;
case OBJECT_TRANSFORM:
- tag = "DROP TRANSFORM";
+ tag = COMMANDTAG_DROP_TRANSFORM;
break;
case OBJECT_ACCESS_METHOD:
- tag = "DROP ACCESS METHOD";
+ tag = COMMANDTAG_DROP_ACCESS_METHOD;
break;
case OBJECT_PUBLICATION:
- tag = "DROP PUBLICATION";
+ tag = COMMANDTAG_DROP_PUBLICATION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "DROP STATISTICS";
+ tag = COMMANDTAG_DROP_STATISTICS;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_TruncateStmt:
- tag = "TRUNCATE TABLE";
+ tag = COMMANDTAG_TRUNCATE_TABLE;
break;
case T_CommentStmt:
- tag = "COMMENT";
+ tag = COMMANDTAG_COMMENT;
break;
case T_SecLabelStmt:
- tag = "SECURITY LABEL";
+ tag = COMMANDTAG_SECURITY_LABEL;
break;
case T_CopyStmt:
- tag = "COPY";
+ tag = COMMANDTAG_COPY;
break;
case T_RenameStmt:
@@ -2584,23 +2593,23 @@ CreateCommandTag(Node *parsetree)
break;
case T_AlterDomainStmt:
- tag = "ALTER DOMAIN";
+ tag = COMMANDTAG_ALTER_DOMAIN;
break;
case T_AlterFunctionStmt:
switch (((AlterFunctionStmt *) parsetree)->objtype)
{
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = COMMANDTAG_ALTER_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = COMMANDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = COMMANDTAG_ALTER_ROUTINE;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
@@ -2608,7 +2617,7 @@ CreateCommandTag(Node *parsetree)
{
GrantStmt *stmt = (GrantStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
+ tag = (stmt->is_grant) ? COMMANDTAG_GRANT : COMMANDTAG_REVOKE;
}
break;
@@ -2616,145 +2625,145 @@ CreateCommandTag(Node *parsetree)
{
GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
+ tag = (stmt->is_grant) ? COMMANDTAG_GRANT_ROLE : COMMANDTAG_REVOKE_ROLE;
}
break;
case T_AlterDefaultPrivilegesStmt:
- tag = "ALTER DEFAULT PRIVILEGES";
+ tag = COMMANDTAG_ALTER_DEFAULT_PRIVILEGES;
break;
case T_DefineStmt:
switch (((DefineStmt *) parsetree)->kind)
{
case OBJECT_AGGREGATE:
- tag = "CREATE AGGREGATE";
+ tag = COMMANDTAG_CREATE_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "CREATE OPERATOR";
+ tag = COMMANDTAG_CREATE_OPERATOR;
break;
case OBJECT_TYPE:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case OBJECT_TSPARSER:
- tag = "CREATE TEXT SEARCH PARSER";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "CREATE TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "CREATE TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "CREATE TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_COLLATION:
- tag = "CREATE COLLATION";
+ tag = COMMANDTAG_CREATE_COLLATION;
break;
case OBJECT_ACCESS_METHOD:
- tag = "CREATE ACCESS METHOD";
+ tag = COMMANDTAG_CREATE_ACCESS_METHOD;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_CompositeTypeStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_CreateEnumStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_CreateRangeStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_AlterEnumStmt:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case T_ViewStmt:
- tag = "CREATE VIEW";
+ tag = COMMANDTAG_CREATE_VIEW;
break;
case T_CreateFunctionStmt:
if (((CreateFunctionStmt *) parsetree)->is_procedure)
- tag = "CREATE PROCEDURE";
+ tag = COMMANDTAG_CREATE_PROCEDURE;
else
- tag = "CREATE FUNCTION";
+ tag = COMMANDTAG_CREATE_FUNCTION;
break;
case T_IndexStmt:
- tag = "CREATE INDEX";
+ tag = COMMANDTAG_CREATE_INDEX;
break;
case T_RuleStmt:
- tag = "CREATE RULE";
+ tag = COMMANDTAG_CREATE_RULE;
break;
case T_CreateSeqStmt:
- tag = "CREATE SEQUENCE";
+ tag = COMMANDTAG_CREATE_SEQUENCE;
break;
case T_AlterSeqStmt:
- tag = "ALTER SEQUENCE";
+ tag = COMMANDTAG_ALTER_SEQUENCE;
break;
case T_DoStmt:
- tag = "DO";
+ tag = COMMANDTAG_DO;
break;
case T_CreatedbStmt:
- tag = "CREATE DATABASE";
+ tag = COMMANDTAG_CREATE_DATABASE;
break;
case T_AlterDatabaseStmt:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case T_AlterDatabaseSetStmt:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case T_DropdbStmt:
- tag = "DROP DATABASE";
+ tag = COMMANDTAG_DROP_DATABASE;
break;
case T_NotifyStmt:
- tag = "NOTIFY";
+ tag = COMMANDTAG_NOTIFY;
break;
case T_ListenStmt:
- tag = "LISTEN";
+ tag = COMMANDTAG_LISTEN;
break;
case T_UnlistenStmt:
- tag = "UNLISTEN";
+ tag = COMMANDTAG_UNLISTEN;
break;
case T_LoadStmt:
- tag = "LOAD";
+ tag = COMMANDTAG_LOAD;
break;
case T_CallStmt:
- tag = "CALL";
+ tag = COMMANDTAG_CALL;
break;
case T_ClusterStmt:
- tag = "CLUSTER";
+ tag = COMMANDTAG_CLUSTER;
break;
case T_VacuumStmt:
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
- tag = "VACUUM";
+ tag = COMMANDTAG_VACUUM;
else
- tag = "ANALYZE";
+ tag = COMMANDTAG_ANALYZE;
break;
case T_ExplainStmt:
- tag = "EXPLAIN";
+ tag = COMMANDTAG_EXPLAIN;
break;
case T_CreateTableAsStmt:
@@ -2762,24 +2771,24 @@ CreateCommandTag(Node *parsetree)
{
case OBJECT_TABLE:
if (((CreateTableAsStmt *) parsetree)->is_select_into)
- tag = "SELECT INTO";
+ tag = COMMANDTAG_SELECT_INTO;
else
- tag = "CREATE TABLE AS";
+ tag = COMMANDTAG_CREATE_TABLE_AS;
break;
case OBJECT_MATVIEW:
- tag = "CREATE MATERIALIZED VIEW";
+ tag = COMMANDTAG_CREATE_MATERIALIZED_VIEW;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_RefreshMatViewStmt:
- tag = "REFRESH MATERIALIZED VIEW";
+ tag = COMMANDTAG_REFRESH_MATERIALIZED_VIEW;
break;
case T_AlterSystemStmt:
- tag = "ALTER SYSTEM";
+ tag = COMMANDTAG_ALTER_SYSTEM;
break;
case T_VariableSetStmt:
@@ -2789,183 +2798,183 @@ CreateCommandTag(Node *parsetree)
case VAR_SET_CURRENT:
case VAR_SET_DEFAULT:
case VAR_SET_MULTI:
- tag = "SET";
+ tag = COMMANDTAG_SET;
break;
case VAR_RESET:
case VAR_RESET_ALL:
- tag = "RESET";
+ tag = COMMANDTAG_RESET;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_VariableShowStmt:
- tag = "SHOW";
+ tag = COMMANDTAG_SHOW;
break;
case T_DiscardStmt:
switch (((DiscardStmt *) parsetree)->target)
{
case DISCARD_ALL:
- tag = "DISCARD ALL";
+ tag = COMMANDTAG_DISCARD_ALL;
break;
case DISCARD_PLANS:
- tag = "DISCARD PLANS";
+ tag = COMMANDTAG_DISCARD_PLANS;
break;
case DISCARD_TEMP:
- tag = "DISCARD TEMP";
+ tag = COMMANDTAG_DISCARD_TEMP;
break;
case DISCARD_SEQUENCES:
- tag = "DISCARD SEQUENCES";
+ tag = COMMANDTAG_DISCARD_SEQUENCES;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_CreateTransformStmt:
- tag = "CREATE TRANSFORM";
+ tag = COMMANDTAG_CREATE_TRANSFORM;
break;
case T_CreateTrigStmt:
- tag = "CREATE TRIGGER";
+ tag = COMMANDTAG_CREATE_TRIGGER;
break;
case T_CreateEventTrigStmt:
- tag = "CREATE EVENT TRIGGER";
+ tag = COMMANDTAG_CREATE_EVENT_TRIGGER;
break;
case T_AlterEventTrigStmt:
- tag = "ALTER EVENT TRIGGER";
+ tag = COMMANDTAG_ALTER_EVENT_TRIGGER;
break;
case T_CreatePLangStmt:
- tag = "CREATE LANGUAGE";
+ tag = COMMANDTAG_CREATE_LANGUAGE;
break;
case T_CreateRoleStmt:
- tag = "CREATE ROLE";
+ tag = COMMANDTAG_CREATE_ROLE;
break;
case T_AlterRoleStmt:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case T_AlterRoleSetStmt:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case T_DropRoleStmt:
- tag = "DROP ROLE";
+ tag = COMMANDTAG_DROP_ROLE;
break;
case T_DropOwnedStmt:
- tag = "DROP OWNED";
+ tag = COMMANDTAG_DROP_OWNED;
break;
case T_ReassignOwnedStmt:
- tag = "REASSIGN OWNED";
+ tag = COMMANDTAG_REASSIGN_OWNED;
break;
case T_LockStmt:
- tag = "LOCK TABLE";
+ tag = COMMANDTAG_LOCK_TABLE;
break;
case T_ConstraintsSetStmt:
- tag = "SET CONSTRAINTS";
+ tag = COMMANDTAG_SET_CONSTRAINTS;
break;
case T_CheckPointStmt:
- tag = "CHECKPOINT";
+ tag = COMMANDTAG_CHECKPOINT;
break;
case T_ReindexStmt:
- tag = "REINDEX";
+ tag = COMMANDTAG_REINDEX;
break;
case T_CreateConversionStmt:
- tag = "CREATE CONVERSION";
+ tag = COMMANDTAG_CREATE_CONVERSION;
break;
case T_CreateCastStmt:
- tag = "CREATE CAST";
+ tag = COMMANDTAG_CREATE_CAST;
break;
case T_CreateOpClassStmt:
- tag = "CREATE OPERATOR CLASS";
+ tag = COMMANDTAG_CREATE_OPERATOR_CLASS;
break;
case T_CreateOpFamilyStmt:
- tag = "CREATE OPERATOR FAMILY";
+ tag = COMMANDTAG_CREATE_OPERATOR_FAMILY;
break;
case T_AlterOpFamilyStmt:
- tag = "ALTER OPERATOR FAMILY";
+ tag = COMMANDTAG_ALTER_OPERATOR_FAMILY;
break;
case T_AlterOperatorStmt:
- tag = "ALTER OPERATOR";
+ tag = COMMANDTAG_ALTER_OPERATOR;
break;
case T_AlterTSDictionaryStmt:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case T_AlterTSConfigurationStmt:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case T_CreatePolicyStmt:
- tag = "CREATE POLICY";
+ tag = COMMANDTAG_CREATE_POLICY;
break;
case T_AlterPolicyStmt:
- tag = "ALTER POLICY";
+ tag = COMMANDTAG_ALTER_POLICY;
break;
case T_CreateAmStmt:
- tag = "CREATE ACCESS METHOD";
+ tag = COMMANDTAG_CREATE_ACCESS_METHOD;
break;
case T_CreatePublicationStmt:
- tag = "CREATE PUBLICATION";
+ tag = COMMANDTAG_CREATE_PUBLICATION;
break;
case T_AlterPublicationStmt:
- tag = "ALTER PUBLICATION";
+ tag = COMMANDTAG_ALTER_PUBLICATION;
break;
case T_CreateSubscriptionStmt:
- tag = "CREATE SUBSCRIPTION";
+ tag = COMMANDTAG_CREATE_SUBSCRIPTION;
break;
case T_AlterSubscriptionStmt:
- tag = "ALTER SUBSCRIPTION";
+ tag = COMMANDTAG_ALTER_SUBSCRIPTION;
break;
case T_DropSubscriptionStmt:
- tag = "DROP SUBSCRIPTION";
+ tag = COMMANDTAG_DROP_SUBSCRIPTION;
break;
case T_AlterCollationStmt:
- tag = "ALTER COLLATION";
+ tag = COMMANDTAG_ALTER_COLLATION;
break;
case T_PrepareStmt:
- tag = "PREPARE";
+ tag = COMMANDTAG_PREPARE;
break;
case T_ExecuteStmt:
- tag = "EXECUTE";
+ tag = COMMANDTAG_EXECUTE;
break;
case T_CreateStatsStmt:
- tag = "CREATE STATISTICS";
+ tag = COMMANDTAG_CREATE_STATISTICS;
break;
case T_AlterStatsStmt:
- tag = "ALTER STATISTICS";
+ tag = COMMANDTAG_ALTER_STATISTICS;
break;
case T_DeallocateStmt:
@@ -2973,9 +2982,9 @@ CreateCommandTag(Node *parsetree)
DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
if (stmt->name == NULL)
- tag = "DEALLOCATE ALL";
+ tag = COMMANDTAG_DEALLOCATE_ALL;
else
- tag = "DEALLOCATE";
+ tag = COMMANDTAG_DEALLOCATE;
}
break;
@@ -2999,33 +3008,33 @@ CreateCommandTag(Node *parsetree)
switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = COMMANDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = COMMANDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
}
}
else
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3033,7 +3042,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
@@ -3059,33 +3068,33 @@ CreateCommandTag(Node *parsetree)
switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = COMMANDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = COMMANDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
else
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3093,7 +3102,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
@@ -3102,7 +3111,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 1b63048a77..a960d7ca80 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -51,7 +51,7 @@ static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD;
static void BuildEventTriggerCache(void);
static void InvalidateEventCacheCallback(Datum arg,
int cacheid, uint32 hashvalue);
-static int DecodeTextArrayToCString(Datum array, char ***cstringp);
+static void DecodeTextArrayToBitmapset(Datum array, Bitmapset **bms);
/*
* Search the event cache by trigger event.
@@ -180,10 +180,7 @@ BuildEventTriggerCache(void)
evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags,
RelationGetDescr(rel), &evttags_isnull);
if (!evttags_isnull)
- {
- item->ntags = DecodeTextArrayToCString(evttags, &item->tag);
- qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp);
- }
+ DecodeTextArrayToBitmapset(evttags, &item->tagset);
/* Add to cache entry. */
entry = hash_search(cache, &event, HASH_ENTER, &found);
@@ -215,18 +212,18 @@ BuildEventTriggerCache(void)
}
/*
- * Decode text[] to an array of C strings.
+ * Decode text[] to a Bitmapset of CommandTags.
*
* We could avoid a bit of overhead here if we were willing to duplicate some
* of the logic from deconstruct_array, but it doesn't seem worth the code
* complexity.
*/
-static int
-DecodeTextArrayToCString(Datum array, char ***cstringp)
+static void
+DecodeTextArrayToBitmapset(Datum array, Bitmapset **tagset)
{
ArrayType *arr = DatumGetArrayTypeP(array);
Datum *elems;
- char **cstring;
+ Bitmapset *bms;
int i;
int nelems;
@@ -234,13 +231,15 @@ DecodeTextArrayToCString(Datum array, char ***cstringp)
elog(ERROR, "expected 1-D text array");
deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems);
- cstring = palloc(nelems * sizeof(char *));
- for (i = 0; i < nelems; ++i)
- cstring[i] = TextDatumGetCString(elems[i]);
+ for (bms = NULL, i = 0; i < nelems; ++i)
+ {
+ char *str = TextDatumGetCString(elems[i]);
+ bms = bms_add_member(bms, GetCommandTagEnum(str));
+ pfree(str);
+ }
pfree(elems);
- *cstringp = cstring;
- return nelems;
+ *tagset = bms;
}
/*
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index c47be0ba4c..53401bd4e9 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -163,7 +163,7 @@ InitPlanCache(void)
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
MemoryContext source_context;
@@ -246,7 +246,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
CachedPlanSource *
CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
diff --git a/src/backend/utils/misc/.gitignore b/src/backend/utils/misc/.gitignore
index 495b1aec76..e9cb531f4f 100644
--- a/src/backend/utils/misc/.gitignore
+++ b/src/backend/utils/misc/.gitignore
@@ -1 +1,2 @@
/guc-file.c
+/commandtag-stamp
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index 2397fc2453..f6e89e820c 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = \
+ commandtag.o \
guc.o \
help_config.o \
pg_config.o \
@@ -39,7 +40,20 @@ include $(top_srcdir)/src/backend/common.mk
# guc-file is compiled as part of guc
guc.o: guc-file.c
+commandtag-stamp: reconcile_commandtag_enum.pl $(top_srcdir)/src/include/utils/commandtag.h $(top_srcdir)/src/backend/utils/misc/commandtag.c
+ $(PERL) $+
+ touch $@
+
+all: distprep
+
+distprep: commandtag-stamp
+
+commandtag.o: commandtag-stamp
+
# Note: guc-file.c is not deleted by 'make clean',
# since we want to ship it in distribution tarballs.
clean:
@rm -f lex.yy.c
+
+maintainer-clean: clean
+ rm -f commandtag-stamp
diff --git a/src/backend/utils/misc/commandtag.c b/src/backend/utils/misc/commandtag.c
new file mode 100644
index 0000000000..18ea937b79
--- /dev/null
+++ b/src/backend/utils/misc/commandtag.c
@@ -0,0 +1,775 @@
+/*-------------------------------------------------------------------------
+ *
+ * commandtag.c
+ * Data and routines for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/commandtag.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "c.h"
+#include "utils/commandtag.h"
+
+/*
+ * We rely on unspecified fields being initialized to 0, false, or NULL.
+ * For brevity, only specify non-zero, non-false, non-NULL values.
+ *
+ * This array, and its associated enum in commandtag.h, are automatically
+ * checked by reconcile_commandtag_enum.pl. That script has fairly rigid
+ * expectations about the exact format of this struct. If you change the
+ * formatting, perhaps by adding comments, you will have to update the
+ * perl script to match. It is best therefore to make comments outside
+ * the boundaries of the array.
+ */
+const CommandTagBehavior tag_behavior[] = {
+ [COMMANDTAG_UNKNOWN] = {
+ .name = "???"
+ },
+ [COMMANDTAG_ALTER_ACCESS_METHOD] = {
+ .name = "ALTER ACCESS METHOD",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_AGGREGATE] = {
+ .name = "ALTER AGGREGATE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_CAST] = {
+ .name = "ALTER CAST",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_COLLATION] = {
+ .name = "ALTER COLLATION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_CONSTRAINT] = {
+ .name = "ALTER CONSTRAINT",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_CONVERSION] = {
+ .name = "ALTER CONVERSION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_DATABASE] = {
+ .name = "ALTER DATABASE"
+ },
+ [COMMANDTAG_ALTER_DEFAULT_PRIVILEGES] = {
+ .name = "ALTER DEFAULT PRIVILEGES",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_DOMAIN] = {
+ .name = "ALTER DOMAIN",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_EVENT_TRIGGER] = {
+ .name = "ALTER EVENT TRIGGER"
+ },
+ [COMMANDTAG_ALTER_EXTENSION] = {
+ .name = "ALTER EXTENSION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER] = {
+ .name = "ALTER FOREIGN DATA WRAPPER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_FOREIGN_TABLE] = {
+ .name = "ALTER FOREIGN TABLE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_FUNCTION] = {
+ .name = "ALTER FUNCTION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_INDEX] = {
+ .name = "ALTER INDEX",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_LANGUAGE] = {
+ .name = "ALTER LANGUAGE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_LARGE_OBJECT] = {
+ .name = "ALTER LARGE OBJECT",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_MATERIALIZED_VIEW] = {
+ .name = "ALTER MATERIALIZED VIEW",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_OPERATOR] = {
+ .name = "ALTER OPERATOR",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_OPERATOR_CLASS] = {
+ .name = "ALTER OPERATOR CLASS",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_OPERATOR_FAMILY] = {
+ .name = "ALTER OPERATOR FAMILY",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_POLICY] = {
+ .name = "ALTER POLICY",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_PROCEDURE] = {
+ .name = "ALTER PROCEDURE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_PUBLICATION] = {
+ .name = "ALTER PUBLICATION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_ROLE] = {
+ .name = "ALTER ROLE"
+ },
+ [COMMANDTAG_ALTER_ROUTINE] = {
+ .name = "ALTER ROUTINE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_RULE] = {
+ .name = "ALTER RULE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_SCHEMA] = {
+ .name = "ALTER SCHEMA",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_SEQUENCE] = {
+ .name = "ALTER SEQUENCE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_SERVER] = {
+ .name = "ALTER SERVER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_STATISTICS] = {
+ .name = "ALTER STATISTICS",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_SUBSCRIPTION] = {
+ .name = "ALTER SUBSCRIPTION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_SYSTEM] = {
+ .name = "ALTER SYSTEM"
+ },
+ [COMMANDTAG_ALTER_TABLE] = {
+ .name = "ALTER TABLE",
+ .event_trigger_ok = true,
+ .table_rewrite_ok = true
+ },
+ [COMMANDTAG_ALTER_TABLESPACE] = {
+ .name = "ALTER TABLESPACE"
+ },
+ [COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION] = {
+ .name = "ALTER TEXT SEARCH CONFIGURATION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY] = {
+ .name = "ALTER TEXT SEARCH DICTIONARY",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_TEXT_SEARCH_PARSER] = {
+ .name = "ALTER TEXT SEARCH PARSER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE] = {
+ .name = "ALTER TEXT SEARCH TEMPLATE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_TRANSFORM] = {
+ .name = "ALTER TRANSFORM",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_TRIGGER] = {
+ .name = "ALTER TRIGGER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_TYPE] = {
+ .name = "ALTER TYPE",
+ .event_trigger_ok = true,
+ .table_rewrite_ok = true
+ },
+ [COMMANDTAG_ALTER_USER_MAPPING] = {
+ .name = "ALTER USER MAPPING",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ALTER_VIEW] = {
+ .name = "ALTER VIEW",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_ANALYZE] = {
+ .name = "ANALYZE"
+ },
+ [COMMANDTAG_BEGIN] = {
+ .name = "BEGIN"
+ },
+ [COMMANDTAG_CALL] = {
+ .name = "CALL"
+ },
+ [COMMANDTAG_CHECKPOINT] = {
+ .name = "CHECKPOINT"
+ },
+ [COMMANDTAG_CLOSE] = {
+ .name = "CLOSE"
+ },
+ [COMMANDTAG_CLOSE_CURSOR] = {
+ .name = "CLOSE CURSOR"
+ },
+ [COMMANDTAG_CLOSE_CURSOR_ALL] = {
+ .name = "CLOSE CURSOR ALL"
+ },
+ [COMMANDTAG_CLUSTER] = {
+ .name = "CLUSTER"
+ },
+ [COMMANDTAG_COMMENT] = {
+ .name = "COMMENT",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_COMMIT] = {
+ .name = "COMMIT"
+ },
+ [COMMANDTAG_COMMIT_PREPARED] = {
+ .name = "COMMIT PREPARED"
+ },
+ [COMMANDTAG_COPY] = {
+ .name = "COPY",
+ .display_rowcount = true
+ },
+ [COMMANDTAG_COPY_FROM] = {
+ .name = "COPY FROM"
+ },
+ [COMMANDTAG_CREATE_ACCESS_METHOD] = {
+ .name = "CREATE ACCESS METHOD",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_AGGREGATE] = {
+ .name = "CREATE AGGREGATE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_CAST] = {
+ .name = "CREATE CAST",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_COLLATION] = {
+ .name = "CREATE COLLATION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_CONSTRAINT] = {
+ .name = "CREATE CONSTRAINT",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_CONVERSION] = {
+ .name = "CREATE CONVERSION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_DATABASE] = {
+ .name = "CREATE DATABASE"
+ },
+ [COMMANDTAG_CREATE_DOMAIN] = {
+ .name = "CREATE DOMAIN",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_EVENT_TRIGGER] = {
+ .name = "CREATE EVENT TRIGGER"
+ },
+ [COMMANDTAG_CREATE_EXTENSION] = {
+ .name = "CREATE EXTENSION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER] = {
+ .name = "CREATE FOREIGN DATA WRAPPER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_FOREIGN_TABLE] = {
+ .name = "CREATE FOREIGN TABLE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_FUNCTION] = {
+ .name = "CREATE FUNCTION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_INDEX] = {
+ .name = "CREATE INDEX",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_LANGUAGE] = {
+ .name = "CREATE LANGUAGE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_MATERIALIZED_VIEW] = {
+ .name = "CREATE MATERIALIZED VIEW",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_OPERATOR] = {
+ .name = "CREATE OPERATOR",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_OPERATOR_CLASS] = {
+ .name = "CREATE OPERATOR CLASS",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_OPERATOR_FAMILY] = {
+ .name = "CREATE OPERATOR FAMILY",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_POLICY] = {
+ .name = "CREATE POLICY",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_PROCEDURE] = {
+ .name = "CREATE PROCEDURE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_PUBLICATION] = {
+ .name = "CREATE PUBLICATION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_ROLE] = {
+ .name = "CREATE ROLE"
+ },
+ [COMMANDTAG_CREATE_ROUTINE] = {
+ .name = "CREATE ROUTINE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_RULE] = {
+ .name = "CREATE RULE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_SCHEMA] = {
+ .name = "CREATE SCHEMA",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_SEQUENCE] = {
+ .name = "CREATE SEQUENCE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_SERVER] = {
+ .name = "CREATE SERVER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_STATISTICS] = {
+ .name = "CREATE STATISTICS",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_SUBSCRIPTION] = {
+ .name = "CREATE SUBSCRIPTION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_TABLE] = {
+ .name = "CREATE TABLE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_TABLE_AS] = {
+ .name = "CREATE TABLE AS",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_TABLESPACE] = {
+ .name = "CREATE TABLESPACE"
+ },
+ [COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION] = {
+ .name = "CREATE TEXT SEARCH CONFIGURATION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY] = {
+ .name = "CREATE TEXT SEARCH DICTIONARY",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_TEXT_SEARCH_PARSER] = {
+ .name = "CREATE TEXT SEARCH PARSER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE] = {
+ .name = "CREATE TEXT SEARCH TEMPLATE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_TRANSFORM] = {
+ .name = "CREATE TRANSFORM",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_TRIGGER] = {
+ .name = "CREATE TRIGGER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_TYPE] = {
+ .name = "CREATE TYPE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_USER_MAPPING] = {
+ .name = "CREATE USER MAPPING",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_CREATE_VIEW] = {
+ .name = "CREATE VIEW",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DEALLOCATE] = {
+ .name = "DEALLOCATE"
+ },
+ [COMMANDTAG_DEALLOCATE_ALL] = {
+ .name = "DEALLOCATE ALL"
+ },
+ [COMMANDTAG_DECLARE_CURSOR] = {
+ .name = "DECLARE CURSOR"
+ },
+ [COMMANDTAG_DELETE] = {
+ .name = "DELETE",
+ .display_rowcount = true
+ },
+ [COMMANDTAG_DISCARD] = {
+ .name = "DISCARD"
+ },
+ [COMMANDTAG_DISCARD_ALL] = {
+ .name = "DISCARD ALL"
+ },
+ [COMMANDTAG_DISCARD_PLANS] = {
+ .name = "DISCARD PLANS"
+ },
+ [COMMANDTAG_DISCARD_SEQUENCES] = {
+ .name = "DISCARD SEQUENCES"
+ },
+ [COMMANDTAG_DISCARD_TEMP] = {
+ .name = "DISCARD TEMP"
+ },
+ [COMMANDTAG_DO] = {
+ .name = "DO"
+ },
+ [COMMANDTAG_DROP_ACCESS_METHOD] = {
+ .name = "DROP ACCESS METHOD",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_AGGREGATE] = {
+ .name = "DROP AGGREGATE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_CAST] = {
+ .name = "DROP CAST",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_COLLATION] = {
+ .name = "DROP COLLATION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_CONSTRAINT] = {
+ .name = "DROP CONSTRAINT",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_CONVERSION] = {
+ .name = "DROP CONVERSION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_DATABASE] = {
+ .name = "DROP DATABASE"
+ },
+ [COMMANDTAG_DROP_DOMAIN] = {
+ .name = "DROP DOMAIN",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_EVENT_TRIGGER] = {
+ .name = "DROP EVENT TRIGGER"
+ },
+ [COMMANDTAG_DROP_EXTENSION] = {
+ .name = "DROP EXTENSION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER] = {
+ .name = "DROP FOREIGN DATA WRAPPER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_FOREIGN_TABLE] = {
+ .name = "DROP FOREIGN TABLE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_FUNCTION] = {
+ .name = "DROP FUNCTION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_INDEX] = {
+ .name = "DROP INDEX",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_LANGUAGE] = {
+ .name = "DROP LANGUAGE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_MATERIALIZED_VIEW] = {
+ .name = "DROP MATERIALIZED VIEW",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_OPERATOR] = {
+ .name = "DROP OPERATOR",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_OPERATOR_CLASS] = {
+ .name = "DROP OPERATOR CLASS",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_OPERATOR_FAMILY] = {
+ .name = "DROP OPERATOR FAMILY",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_OWNED] = {
+ .name = "DROP OWNED",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_POLICY] = {
+ .name = "DROP POLICY",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_PROCEDURE] = {
+ .name = "DROP PROCEDURE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_PUBLICATION] = {
+ .name = "DROP PUBLICATION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_REPLICATION_SLOT] = {
+ .name = "DROP REPLICATION SLOT"
+ },
+ [COMMANDTAG_DROP_ROLE] = {
+ .name = "DROP ROLE"
+ },
+ [COMMANDTAG_DROP_ROUTINE] = {
+ .name = "DROP ROUTINE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_RULE] = {
+ .name = "DROP RULE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_SCHEMA] = {
+ .name = "DROP SCHEMA",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_SEQUENCE] = {
+ .name = "DROP SEQUENCE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_SERVER] = {
+ .name = "DROP SERVER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_STATISTICS] = {
+ .name = "DROP STATISTICS",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_SUBSCRIPTION] = {
+ .name = "DROP SUBSCRIPTION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_TABLE] = {
+ .name = "DROP TABLE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_TABLESPACE] = {
+ .name = "DROP TABLESPACE"
+ },
+ [COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION] = {
+ .name = "DROP TEXT SEARCH CONFIGURATION",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY] = {
+ .name = "DROP TEXT SEARCH DICTIONARY",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_TEXT_SEARCH_PARSER] = {
+ .name = "DROP TEXT SEARCH PARSER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE] = {
+ .name = "DROP TEXT SEARCH TEMPLATE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_TRANSFORM] = {
+ .name = "DROP TRANSFORM",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_TRIGGER] = {
+ .name = "DROP TRIGGER",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_TYPE] = {
+ .name = "DROP TYPE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_USER_MAPPING] = {
+ .name = "DROP USER MAPPING",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_DROP_VIEW] = {
+ .name = "DROP VIEW",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_EXECUTE] = {
+ .name = "EXECUTE"
+ },
+ [COMMANDTAG_EXPLAIN] = {
+ .name = "EXPLAIN"
+ },
+ [COMMANDTAG_FETCH] = {
+ .name = "FETCH",
+ .display_rowcount = true
+ },
+ [COMMANDTAG_GRANT] = {
+ .name = "GRANT",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_GRANT_ROLE] = {
+ .name = "GRANT ROLE"
+ },
+ [COMMANDTAG_IMPORT_FOREIGN_SCHEMA] = {
+ .name = "IMPORT FOREIGN SCHEMA",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_INSERT] = {
+ .name = "INSERT",
+ .display_last_oid = true,
+ .display_rowcount = true
+ },
+ [COMMANDTAG_LISTEN] = {
+ .name = "LISTEN"
+ },
+ [COMMANDTAG_LOAD] = {
+ .name = "LOAD"
+ },
+ [COMMANDTAG_LOCK_TABLE] = {
+ .name = "LOCK TABLE"
+ },
+ [COMMANDTAG_MOVE] = {
+ .name = "MOVE",
+ .display_rowcount = true
+ },
+ [COMMANDTAG_NOTIFY] = {
+ .name = "NOTIFY"
+ },
+ [COMMANDTAG_PREPARE] = {
+ .name = "PREPARE"
+ },
+ [COMMANDTAG_PREPARE_TRANSACTION] = {
+ .name = "PREPARE TRANSACTION"
+ },
+ [COMMANDTAG_REASSIGN_OWNED] = {
+ .name = "REASSIGN OWNED"
+ },
+ [COMMANDTAG_REFRESH_MATERIALIZED_VIEW] = {
+ .name = "REFRESH MATERIALIZED VIEW",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_REINDEX] = {
+ .name = "REINDEX"
+ },
+ [COMMANDTAG_RELEASE] = {
+ .name = "RELEASE"
+ },
+ [COMMANDTAG_RESET] = {
+ .name = "RESET"
+ },
+ [COMMANDTAG_REVOKE] = {
+ .name = "REVOKE",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_REVOKE_ROLE] = {
+ .name = "REVOKE ROLE"
+ },
+ [COMMANDTAG_ROLLBACK] = {
+ .name = "ROLLBACK"
+ },
+ [COMMANDTAG_ROLLBACK_PREPARED] = {
+ .name = "ROLLBACK PREPARED"
+ },
+ [COMMANDTAG_SAVEPOINT] = {
+ .name = "SAVEPOINT"
+ },
+ [COMMANDTAG_SECURITY_LABEL] = {
+ .name = "SECURITY LABEL",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_SELECT] = {
+ .name = "SELECT",
+ .display_rowcount = true
+ },
+ [COMMANDTAG_SELECT_FOR_KEY_SHARE] = {
+ .name = "SELECT FOR KEY SHARE"
+ },
+ [COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE] = {
+ .name = "SELECT FOR NO KEY UPDATE"
+ },
+ [COMMANDTAG_SELECT_FOR_SHARE] = {
+ .name = "SELECT FOR SHARE"
+ },
+ [COMMANDTAG_SELECT_FOR_UPDATE] = {
+ .name = "SELECT FOR UPDATE"
+ },
+ [COMMANDTAG_SELECT_INTO] = {
+ .name = "SELECT INTO",
+ .event_trigger_ok = true
+ },
+ [COMMANDTAG_SET] = {
+ .name = "SET"
+ },
+ [COMMANDTAG_SET_CONSTRAINTS] = {
+ .name = "SET CONSTRAINTS"
+ },
+ [COMMANDTAG_SHOW] = {
+ .name = "SHOW"
+ },
+ [COMMANDTAG_START_TRANSACTION] = {
+ .name = "START TRANSACTION"
+ },
+ [COMMANDTAG_TRUNCATE_TABLE] = {
+ .name = "TRUNCATE TABLE"
+ },
+ [COMMANDTAG_UNLISTEN] = {
+ .name = "UNLISTEN"
+ },
+ [COMMANDTAG_UPDATE] = {
+ .name = "UPDATE",
+ .display_rowcount = true
+ },
+ [COMMANDTAG_VACUUM] = {
+ .name = "VACUUM"
+ }
+};
+StaticAssertDecl(FIRST_COMMAND_TAG == (CommandTag)0, "first command tag");
+StaticAssertDecl(lengthof(tag_behavior) == (LAST_COMMAND_TAG + 1),
+ "array length mismatch");
+
+
+static const unsigned int tag_behavior_length = lengthof(tag_behavior);
+
+/*
+ * Search CommandTag by name
+ *
+ * Returns CommandTag, or COMMANDTAG_UNKNOWN if not recognized
+ */
+CommandTag
+GetCommandTagEnum(const char *commandname)
+{
+ const CommandTagBehavior *base, *last, *position;
+ int result;
+
+ if (commandname == NULL || *commandname == '\0')
+ return COMMANDTAG_UNKNOWN;
+
+ base = tag_behavior;
+ last = tag_behavior + tag_behavior_length - 1;
+ while (last >= base)
+ {
+ position = base + ((last - base) >> 1);
+ result = pg_strcasecmp(commandname, position->name);
+ if (result == 0)
+ return (CommandTag) (position - tag_behavior);
+ else if (result < 0)
+ last = position - 1;
+ else
+ base = position + 1;
+ }
+ return COMMANDTAG_UNKNOWN;
+}
diff --git a/src/backend/utils/misc/reconcile_commandtag_enum.pl b/src/backend/utils/misc/reconcile_commandtag_enum.pl
new file mode 100755
index 0000000000..1a484d5546
--- /dev/null
+++ b/src/backend/utils/misc/reconcile_commandtag_enum.pl
@@ -0,0 +1,215 @@
+#!/usr/bin/perl
+#----------------------------------------------------------------------
+#
+# reconcile_commandtag_enum.pl
+# Perl script that reads in commandtag.h and commandtag.c and checks
+# that the ordering of the enum's string representation is proper.
+#
+# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/backend/utils/misc/mkcommandtag.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings;
+use IO::File;
+
+our ($hpath, $cpath) = @ARGV;
+
+my $h = slurp($hpath);
+my $c = slurp($cpath);
+
+our ($FirstCommandTag, $LastCommandTag, @CommandTags, @CommandTagRecords);
+
+PARSE_HFILE:
+{
+ our $first_command_tag;
+ our $last_command_tag;
+ our @command_tags;
+
+ unless ($h =~ m/
+ # Expect the beginning of the CommandTag enum
+ typedef \s+ enum \s+ CommandTag \s* \{
+
+ \s*
+
+ # Expect a #define in this location
+ \#define \s+ FIRST_COMMAND_TAG \s+ (COMMANDTAG_\w+) (?{ local $first_command_tag = $^N })
+
+ \s*
+
+ # Expect an arbitrary number of command tags
+ (?:
+ (COMMANDTAG_\w+)
+ \s*
+ ,?
+ \s*
+ (?{ local @command_tags = (@command_tags, $^N) })
+ )*
+
+ \s*
+
+ # Expect a final #define
+ \#define \s+ LAST_COMMAND_TAG \s+ (COMMANDTAG_\w+) (?{ local $last_command_tag = $^N })
+
+ \s*
+
+ # Expect the end of the enum typedef
+ \} \s+ CommandTag \s* ;
+
+ # Copy everything we parsed to non-local storage
+ (?{
+ $FirstCommandTag = $first_command_tag;
+ $LastCommandTag = $last_command_tag;
+ @CommandTags = @command_tags;
+ })
+ /msx
+ )
+ {
+ die "Failed to parse $hpath";
+ }
+}
+
+PARSE_CFILE:
+{
+ our $next_enum;
+ our $next_name;
+ our $next_display_last_oid;
+ our $next_display_rowcount;
+ our $next_event_trigger_ok;
+ our $next_table_rewrite_ok;
+ our @command_tag_records;
+ unless ($c =~ m/
+ # Expect the beginning of the CommandTagBehavior array
+ const \s+ CommandTagBehavior \s+ tag_behavior \s* \[ \s* \] \s* = \s* \{
+
+ # Expect arbitrarily many command tag records
+ (?:
+ \s* ,? \s*
+ \[ \s* (COMMANDTAG_\w+) \s* \] (?{ local $next_enum = $^N })
+ \s*
+ =
+ \s*
+ \{
+ (?{ undef local $next_name;
+ undef local $next_display_last_oid;
+ undef local $next_display_rowcount;
+ undef local $next_event_trigger_ok;
+ undef local $next_table_rewrite_ok;
+ })
+ \s*
+ (?:
+ (?:
+ # Possibly a name field
+ (?:
+ \.name \s* = \s* "([^"]*)"
+ (?{ local $next_name = $^N })
+ )
+ |
+ # Possibly an display_last_oid field
+ (?:
+ \.display_last_oid \s* = \s* ((?i:\w+))
+ (?{ local $next_display_last_oid = $^N })
+ )
+ |
+ # Possibly an display_rowcount field
+ (?:
+ \.display_rowcount \s* = \s* ((?i:\w+))
+ (?{ local $next_display_rowcount = $^N })
+ )
+ |
+ # Possibly an event_trigger_ok field
+ (?:
+ \.event_trigger_ok \s* = \s* ((?i:\w+))
+ (?{ local $next_event_trigger_ok = $^N })
+ )
+ |
+ # Possibly an table_rewrite_ok field
+ (?:
+ \.table_rewrite_ok \s* = \s* ((?i:\w+))
+ (?{ local $next_table_rewrite_ok = $^N })
+ )
+ )
+ \s*
+ ,?
+ \s*
+ )*
+ \s*
+ \}
+ \s* ,? \s*
+ (?{ local @command_tag_records =
+ (@command_tag_records,
+ {
+ defined $next_enum ? (enum => $next_enum) : (),
+ defined $next_name ? (name => $next_name) : (),
+ defined $next_display_last_oid ? (display_last_oid => $next_display_last_oid) : (),
+ defined $next_display_rowcount ? (display_rowcount => $next_display_rowcount) : (),
+ defined $next_event_trigger_ok ? (event_trigger_ok => $next_event_trigger_ok) : (),
+ defined $next_table_rewrite_ok ? (table_rewrite_ok => $next_table_rewrite_ok) : (),
+ }
+ )
+ })
+ )*
+
+ # Expect the end of the CommandTagBehavior array
+ \s*
+ \}
+ (?{ @CommandTagRecords = @command_tag_records })
+ /msx
+ )
+ {
+ die "Failed to parse $cpath";
+ }
+}
+
+my $hcnt = scalar(@CommandTags);
+my $ccnt = scalar(@CommandTagRecords);
+if ($hcnt != $ccnt)
+{
+ die "CommandTag enum mismatch: $hcnt enumerated values in $hpath, but $ccnt corresponding records in $cpath";
+}
+for (my $i = 0; $i < $hcnt; $i++)
+{
+ my $h_enum = $CommandTags[$i];
+ my $c_enum = $CommandTagRecords[$i]->{'enum'};
+ my $i_name = $CommandTagRecords[$i]->{'name'};
+ die "CommandTag enum misordered: \"$h_enum\" vs. \"$c_enum\" in position $i" unless($h_enum eq $c_enum);
+ die "CommandTag enum \"$c_enum\" missing name field" unless defined $i_name;
+
+ foreach my $opt (qw(display_last_oid display_rowcount event_trigger_ok table_rewrite_ok))
+ {
+ if (exists $CommandTagRecords[$i]->{$opt})
+ {
+ my $val = $CommandTagRecords[$i]->{$opt};
+ die "CommandTag enum \"$c_enum\" field $opt: '$val' better written as 'true'"
+ if ($val =~ m/true/i && $val ne 'true');
+ die "CommandTag enum \"$c_enum\" field $opt better left unspecified than specified as 'false'"
+ if ($val =~ m/false/i);
+ die "CommandTag enum \"$c_enum\" field $opt: unexpected value '$val'"
+ unless ($val eq 'true');
+ }
+ }
+ for (my $j = $i+1; $j < $hcnt; $j++)
+ {
+ my $j_name = $CommandTagRecords[$j]->{'name'};
+ die "CommandTag enum misordered: \"$i_name\" comes before \"$j_name\"" unless ($i_name lt $j_name);
+ }
+}
+die "FIRST_COMMAND_TAG defined as $FirstCommandTag; expected $CommandTags[0]"
+ unless($FirstCommandTag eq $CommandTags[0]);
+die "LAST_COMMAND_TAG defined as $LastCommandTag; expected $CommandTags[$#CommandTags]"
+ unless($LastCommandTag eq $CommandTags[$#CommandTags]);
+
+sub slurp {
+ my ($path) = @_;
+ my $fh = IO::File->new($path) or die "Cannot read $path: $!";
+ my $contents;
+ {
+ undef(local $/);
+ $contents = <$fh>;
+ }
+ $fh->close();
+ return $contents;
+}
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index b675575c31..43df302f66 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -281,7 +281,7 @@ void
PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan)
{
@@ -289,10 +289,12 @@ PortalDefineQuery(Portal portal,
AssertState(portal->status == PORTAL_NEW);
AssertArg(sourceText != NULL);
- AssertArg(commandTag != NULL || stmts == NIL);
+ AssertArg(commandTag != COMMANDTAG_UNKNOWN || stmts == NIL);
portal->prepStmtName = prepStmtName;
portal->sourceText = sourceText;
+ portal->qcdata.commandTag = commandTag;
+ portal->qcdata.nprocessed = 0;
portal->commandTag = commandTag;
portal->stmts = stmts;
portal->cplan = cplan;
diff --git a/src/include/commands/createas.h b/src/include/commands/createas.h
index 7743851a38..6e6bb8e567 100644
--- a/src/include/commands/createas.h
+++ b/src/include/commands/createas.h
@@ -22,7 +22,7 @@
extern ObjectAddress ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
- ParamListInfo params, QueryEnvironment *queryEnv, char *completionTag);
+ ParamListInfo params, QueryEnvironment *queryEnv, QueryCompletionData *qc);
extern int GetIntoRelEFlags(IntoClause *intoClause);
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index faa2958b89..5123c4b850 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -25,7 +25,7 @@ typedef struct EventTriggerData
NodeTag type;
const char *event; /* event name */
Node *parsetree; /* parse tree */
- const char *tag; /* command tag */
+ CommandTag tag;
} EventTriggerData;
#define AT_REWRITE_ALTER_PERSISTENCE 0x01
diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h
index 6bdb7ca258..12147a8339 100644
--- a/src/include/commands/matview.h
+++ b/src/include/commands/matview.h
@@ -24,7 +24,7 @@
extern void SetMatViewPopulatedState(Relation relation, bool newstate);
extern ObjectAddress ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag);
+ ParamListInfo params, QueryCompletionData *qc);
extern DestReceiver *CreateTransientRelDestReceiver(Oid oid);
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index 4ecc1a2ecd..667cae0a7a 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -23,7 +23,7 @@ extern void PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, Para
bool isTopLevel);
extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
- char *completionTag);
+ QueryCompletionData *qc);
extern void PerformPortalClose(const char *name);
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index a0509e1f33..b1ea4e837a 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -40,7 +40,7 @@ extern void PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
extern void ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
extern void DeallocateQuery(DeallocateStmt *stmt);
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into,
ExplainState *es, const char *queryString,
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f985453ec3..8ec1871c05 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -27,6 +27,7 @@
#include "datatype/timestamp.h" /* for TimestampTz */
#include "pgtime.h" /* for pg_time_t */
+#include "utils/commandtag.h"
#define InvalidPid (-1)
@@ -281,8 +282,11 @@ extern bool stack_is_too_deep(void);
/* in tcop/utility.c */
extern void PreventCommandIfReadOnly(const char *cmdname);
+extern void PreventCommandTagIfReadOnly(CommandTag commandTag);
extern void PreventCommandIfParallelMode(const char *cmdname);
+extern void PreventCommandTagIfParallelMode(CommandTag commandTag);
extern void PreventCommandDuringRecovery(const char *cmdname);
+extern void PreventCommandTagDuringRecovery(CommandTag commandTag);
/* in utils/misc/guc.c */
extern int trace_recovery_messages;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index da0706add5..2c3ae6b603 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -27,6 +27,7 @@
#include "nodes/primnodes.h"
#include "nodes/value.h"
#include "partitioning/partdefs.h"
+#include "utils/commandtag.h"
typedef enum OverridingKind
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 35bce731a1..08ffee0ce7 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -68,6 +68,8 @@
#define DEST_H
#include "executor/tuptable.h"
+#include "utils/commandtag.h"
+#include "utils/querycompletion.h"
/* buffer size to use for command completion tags */
@@ -134,9 +136,10 @@ extern PGDLLIMPORT DestReceiver *None_Receiver; /* permanent receiver for
/* The primary destination management functions */
-extern void BeginCommand(const char *commandTag, CommandDest dest);
+extern void BeginCommand(CommandTag commandTag, CommandDest dest);
extern DestReceiver *CreateDestReceiver(CommandDest dest);
-extern void EndCommand(const char *commandTag, CommandDest dest);
+extern void EndCommand(const QueryCompletionData *qc, CommandDest dest,
+ bool force_undecorated_output);
/* Additional functions that go with destination management, more or less. */
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index 4ad6324e2d..457ba3f415 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.h
@@ -35,7 +35,7 @@ extern void PortalSetResultFormat(Portal portal, int nFormats,
extern bool PortalRun(Portal portal, long count, bool isTopLevel,
bool run_once, DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletionData *qc);
extern uint64 PortalRunFetch(Portal portal,
FetchDirection fdirection,
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index a551e08cb8..e86bcb6e0d 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -15,6 +15,7 @@
#define UTILITY_H
#include "tcop/tcopprot.h"
+#include "utils/querycompletion.h"
typedef enum
{
@@ -71,17 +72,17 @@ typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
const char *queryString, ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletionData *qc);
extern void ProcessUtilityForAlterTable(Node *stmt,
AlterTableUtilityContext *context);
@@ -92,7 +93,13 @@ extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
extern Query *UtilityContainsQuery(Node *parsetree);
-extern const char *CreateCommandTag(Node *parsetree);
+extern CommandTag CreateCommandTag(Node *parsetree);
+
+static inline const char *
+CreateCommandName(Node *parsetree)
+{
+ return GetCommandTagName(CreateCommandTag(parsetree));
+}
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
diff --git a/src/include/utils/commandtag.h b/src/include/utils/commandtag.h
new file mode 100644
index 0000000000..5f2b8face7
--- /dev/null
+++ b/src/include/utils/commandtag.h
@@ -0,0 +1,297 @@
+/*-------------------------------------------------------------------------
+ *
+ * commandtag.h
+ * Declarations for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/commandtag.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMMANDTAG_H
+#define COMMANDTAG_H
+
+/*
+ * BEWARE: These are in sorted order, but ordered by their printed
+ * values in the tag_name list (see common/commandtag.c).
+ * In particular it matters because the sort ordering changes
+ * when you replace a space with an underscore. To wit:
+ *
+ * "CREATE TABLE"
+ * "CREATE TABLE AS"
+ * "CREATE TABLESPACE"
+ *
+ * but...
+ *
+ * CREATE_TABLE
+ * CREATE_TABLESPACE
+ * CREATE_TABLE_AS
+ *
+ * It also matters that COMMANDTAG_UNKNOWN is written "???".
+ *
+ * If you add a value here, add it in common/commandtag.c also, and
+ * be careful to get the ordering right.
+ *
+ * This enum, and its associated array in commandtag.c, are automatically
+ * checked by reconcile_commandtag_enum.pl. That script has fairly rigid
+ * expectations about the exact format of this enum. If you change the
+ * formatting, perhaps by adding comments, you will have to update the
+ * perl script to match. It is best therefore to make comments outside
+ * the boundaries of the typedef.
+ */
+typedef enum CommandTag
+{
+#define FIRST_COMMAND_TAG COMMANDTAG_UNKNOWN
+ COMMANDTAG_UNKNOWN,
+ COMMANDTAG_ALTER_ACCESS_METHOD,
+ COMMANDTAG_ALTER_AGGREGATE,
+ COMMANDTAG_ALTER_CAST,
+ COMMANDTAG_ALTER_COLLATION,
+ COMMANDTAG_ALTER_CONSTRAINT,
+ COMMANDTAG_ALTER_CONVERSION,
+ COMMANDTAG_ALTER_DATABASE,
+ COMMANDTAG_ALTER_DEFAULT_PRIVILEGES,
+ COMMANDTAG_ALTER_DOMAIN,
+ COMMANDTAG_ALTER_EVENT_TRIGGER,
+ COMMANDTAG_ALTER_EXTENSION,
+ COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER,
+ COMMANDTAG_ALTER_FOREIGN_TABLE,
+ COMMANDTAG_ALTER_FUNCTION,
+ COMMANDTAG_ALTER_INDEX,
+ COMMANDTAG_ALTER_LANGUAGE,
+ COMMANDTAG_ALTER_LARGE_OBJECT,
+ COMMANDTAG_ALTER_MATERIALIZED_VIEW,
+ COMMANDTAG_ALTER_OPERATOR,
+ COMMANDTAG_ALTER_OPERATOR_CLASS,
+ COMMANDTAG_ALTER_OPERATOR_FAMILY,
+ COMMANDTAG_ALTER_POLICY,
+ COMMANDTAG_ALTER_PROCEDURE,
+ COMMANDTAG_ALTER_PUBLICATION,
+ COMMANDTAG_ALTER_ROLE,
+ COMMANDTAG_ALTER_ROUTINE,
+ COMMANDTAG_ALTER_RULE,
+ COMMANDTAG_ALTER_SCHEMA,
+ COMMANDTAG_ALTER_SEQUENCE,
+ COMMANDTAG_ALTER_SERVER,
+ COMMANDTAG_ALTER_STATISTICS,
+ COMMANDTAG_ALTER_SUBSCRIPTION,
+ COMMANDTAG_ALTER_SYSTEM,
+ COMMANDTAG_ALTER_TABLE,
+ COMMANDTAG_ALTER_TABLESPACE,
+ COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION,
+ COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY,
+ COMMANDTAG_ALTER_TEXT_SEARCH_PARSER,
+ COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE,
+ COMMANDTAG_ALTER_TRANSFORM,
+ COMMANDTAG_ALTER_TRIGGER,
+ COMMANDTAG_ALTER_TYPE,
+ COMMANDTAG_ALTER_USER_MAPPING,
+ COMMANDTAG_ALTER_VIEW,
+ COMMANDTAG_ANALYZE,
+ COMMANDTAG_BEGIN,
+ COMMANDTAG_CALL,
+ COMMANDTAG_CHECKPOINT,
+ COMMANDTAG_CLOSE,
+ COMMANDTAG_CLOSE_CURSOR,
+ COMMANDTAG_CLOSE_CURSOR_ALL,
+ COMMANDTAG_CLUSTER,
+ COMMANDTAG_COMMENT,
+ COMMANDTAG_COMMIT,
+ COMMANDTAG_COMMIT_PREPARED,
+ COMMANDTAG_COPY,
+ COMMANDTAG_COPY_FROM,
+ COMMANDTAG_CREATE_ACCESS_METHOD,
+ COMMANDTAG_CREATE_AGGREGATE,
+ COMMANDTAG_CREATE_CAST,
+ COMMANDTAG_CREATE_COLLATION,
+ COMMANDTAG_CREATE_CONSTRAINT,
+ COMMANDTAG_CREATE_CONVERSION,
+ COMMANDTAG_CREATE_DATABASE,
+ COMMANDTAG_CREATE_DOMAIN,
+ COMMANDTAG_CREATE_EVENT_TRIGGER,
+ COMMANDTAG_CREATE_EXTENSION,
+ COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER,
+ COMMANDTAG_CREATE_FOREIGN_TABLE,
+ COMMANDTAG_CREATE_FUNCTION,
+ COMMANDTAG_CREATE_INDEX,
+ COMMANDTAG_CREATE_LANGUAGE,
+ COMMANDTAG_CREATE_MATERIALIZED_VIEW,
+ COMMANDTAG_CREATE_OPERATOR,
+ COMMANDTAG_CREATE_OPERATOR_CLASS,
+ COMMANDTAG_CREATE_OPERATOR_FAMILY,
+ COMMANDTAG_CREATE_POLICY,
+ COMMANDTAG_CREATE_PROCEDURE,
+ COMMANDTAG_CREATE_PUBLICATION,
+ COMMANDTAG_CREATE_ROLE,
+ COMMANDTAG_CREATE_ROUTINE,
+ COMMANDTAG_CREATE_RULE,
+ COMMANDTAG_CREATE_SCHEMA,
+ COMMANDTAG_CREATE_SEQUENCE,
+ COMMANDTAG_CREATE_SERVER,
+ COMMANDTAG_CREATE_STATISTICS,
+ COMMANDTAG_CREATE_SUBSCRIPTION,
+ COMMANDTAG_CREATE_TABLE,
+ COMMANDTAG_CREATE_TABLE_AS,
+ COMMANDTAG_CREATE_TABLESPACE,
+ COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION,
+ COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY,
+ COMMANDTAG_CREATE_TEXT_SEARCH_PARSER,
+ COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE,
+ COMMANDTAG_CREATE_TRANSFORM,
+ COMMANDTAG_CREATE_TRIGGER,
+ COMMANDTAG_CREATE_TYPE,
+ COMMANDTAG_CREATE_USER_MAPPING,
+ COMMANDTAG_CREATE_VIEW,
+ COMMANDTAG_DEALLOCATE,
+ COMMANDTAG_DEALLOCATE_ALL,
+ COMMANDTAG_DECLARE_CURSOR,
+ COMMANDTAG_DELETE,
+ COMMANDTAG_DISCARD,
+ COMMANDTAG_DISCARD_ALL,
+ COMMANDTAG_DISCARD_PLANS,
+ COMMANDTAG_DISCARD_SEQUENCES,
+ COMMANDTAG_DISCARD_TEMP,
+ COMMANDTAG_DO,
+ COMMANDTAG_DROP_ACCESS_METHOD,
+ COMMANDTAG_DROP_AGGREGATE,
+ COMMANDTAG_DROP_CAST,
+ COMMANDTAG_DROP_COLLATION,
+ COMMANDTAG_DROP_CONSTRAINT,
+ COMMANDTAG_DROP_CONVERSION,
+ COMMANDTAG_DROP_DATABASE,
+ COMMANDTAG_DROP_DOMAIN,
+ COMMANDTAG_DROP_EVENT_TRIGGER,
+ COMMANDTAG_DROP_EXTENSION,
+ COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER,
+ COMMANDTAG_DROP_FOREIGN_TABLE,
+ COMMANDTAG_DROP_FUNCTION,
+ COMMANDTAG_DROP_INDEX,
+ COMMANDTAG_DROP_LANGUAGE,
+ COMMANDTAG_DROP_MATERIALIZED_VIEW,
+ COMMANDTAG_DROP_OPERATOR,
+ COMMANDTAG_DROP_OPERATOR_CLASS,
+ COMMANDTAG_DROP_OPERATOR_FAMILY,
+ COMMANDTAG_DROP_OWNED,
+ COMMANDTAG_DROP_POLICY,
+ COMMANDTAG_DROP_PROCEDURE,
+ COMMANDTAG_DROP_PUBLICATION,
+ COMMANDTAG_DROP_REPLICATION_SLOT,
+ COMMANDTAG_DROP_ROLE,
+ COMMANDTAG_DROP_ROUTINE,
+ COMMANDTAG_DROP_RULE,
+ COMMANDTAG_DROP_SCHEMA,
+ COMMANDTAG_DROP_SEQUENCE,
+ COMMANDTAG_DROP_SERVER,
+ COMMANDTAG_DROP_STATISTICS,
+ COMMANDTAG_DROP_SUBSCRIPTION,
+ COMMANDTAG_DROP_TABLE,
+ COMMANDTAG_DROP_TABLESPACE,
+ COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION,
+ COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY,
+ COMMANDTAG_DROP_TEXT_SEARCH_PARSER,
+ COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE,
+ COMMANDTAG_DROP_TRANSFORM,
+ COMMANDTAG_DROP_TRIGGER,
+ COMMANDTAG_DROP_TYPE,
+ COMMANDTAG_DROP_USER_MAPPING,
+ COMMANDTAG_DROP_VIEW,
+ COMMANDTAG_EXECUTE,
+ COMMANDTAG_EXPLAIN,
+ COMMANDTAG_FETCH,
+ COMMANDTAG_GRANT,
+ COMMANDTAG_GRANT_ROLE,
+ COMMANDTAG_IMPORT_FOREIGN_SCHEMA,
+ COMMANDTAG_INSERT,
+ COMMANDTAG_LISTEN,
+ COMMANDTAG_LOAD,
+ COMMANDTAG_LOCK_TABLE,
+ COMMANDTAG_MOVE,
+ COMMANDTAG_NOTIFY,
+ COMMANDTAG_PREPARE,
+ COMMANDTAG_PREPARE_TRANSACTION,
+ COMMANDTAG_REASSIGN_OWNED,
+ COMMANDTAG_REFRESH_MATERIALIZED_VIEW,
+ COMMANDTAG_REINDEX,
+ COMMANDTAG_RELEASE,
+ COMMANDTAG_RESET,
+ COMMANDTAG_REVOKE,
+ COMMANDTAG_REVOKE_ROLE,
+ COMMANDTAG_ROLLBACK,
+ COMMANDTAG_ROLLBACK_PREPARED,
+ COMMANDTAG_SAVEPOINT,
+ COMMANDTAG_SECURITY_LABEL,
+ COMMANDTAG_SELECT,
+ COMMANDTAG_SELECT_FOR_KEY_SHARE,
+ COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE,
+ COMMANDTAG_SELECT_FOR_SHARE,
+ COMMANDTAG_SELECT_FOR_UPDATE,
+ COMMANDTAG_SELECT_INTO,
+ COMMANDTAG_SET,
+ COMMANDTAG_SET_CONSTRAINTS,
+ COMMANDTAG_SHOW,
+ COMMANDTAG_START_TRANSACTION,
+ COMMANDTAG_TRUNCATE_TABLE,
+ COMMANDTAG_UNLISTEN,
+ COMMANDTAG_UPDATE,
+ COMMANDTAG_VACUUM
+#define LAST_COMMAND_TAG COMMANDTAG_VACUUM
+} CommandTag;
+
+typedef struct CommandTagBehavior {
+ const char *name;
+ const bool display_last_oid;
+ const bool display_rowcount;
+ const bool event_trigger_ok;
+ const bool table_rewrite_ok;
+} CommandTagBehavior;
+
+extern const CommandTagBehavior tag_behavior[];
+
+static inline bool
+PG_VALID_COMMANDTAG(CommandTag commandTag)
+{
+ return (commandTag >= FIRST_COMMAND_TAG &&
+ commandTag <= LAST_COMMAND_TAG);
+}
+
+static inline const char *
+GetCommandTagName(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].name;
+}
+
+static inline bool
+command_tag_display_last_oid(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].display_last_oid;
+}
+
+static inline bool
+command_tag_display_rowcount(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].display_rowcount;
+}
+
+static inline bool
+command_tag_event_trigger_ok(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].event_trigger_ok;
+}
+
+static inline bool
+command_tag_table_rewrite_ok(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].table_rewrite_ok;
+}
+
+extern CommandTag GetCommandTagEnum(const char *tagname);
+
+#endif /* COMMANDTAG_H */
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index 6c3ff81ba3..bc8ce48061 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -28,8 +28,7 @@ typedef struct
{
Oid fnoid; /* function to be called */
char enabled; /* as SESSION_REPLICATION_ROLE_* */
- int ntags; /* number of command tags */
- char **tag; /* command tags in SORTED order */
+ Bitmapset *tagset; /* command tags, or NULL if empty */
} EventTriggerCacheItem;
extern List *EventCacheLookup(EventTriggerEvent event);
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index e48661ebec..04b02c083b 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -95,7 +95,7 @@ typedef struct CachedPlanSource
int magic; /* should equal CACHEDPLANSOURCE_MAGIC */
struct RawStmt *raw_parse_tree; /* output of raw_parser(), or NULL */
const char *query_string; /* source text of query */
- const char *commandTag; /* command tag (a constant!), or NULL */
+ CommandTag commandTag;
Oid *param_types; /* array of parameter type OIDs, or NULL */
int num_params; /* length of param_types array */
ParserSetupHook parserSetup; /* alternative parameter spec method */
@@ -186,10 +186,10 @@ extern void ResetPlanCache(void);
extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern void CompleteCachedPlan(CachedPlanSource *plansource,
List *querytree_list,
MemoryContext querytree_context,
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 0b69433722..2365883acd 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -48,6 +48,7 @@
#include "datatype/timestamp.h"
#include "executor/execdesc.h"
+#include "utils/commandtag.h"
#include "utils/plancache.h"
#include "utils/resowner.h"
@@ -132,7 +133,8 @@ typedef struct PortalData
/* The query or queries the portal will execute */
const char *sourceText; /* text of query (as of 8.4, never NULL) */
- const char *commandTag; /* command tag for original query */
+ CommandTag commandTag; /* command tag for original query */
+ QueryCompletionData qcdata; /* command completion data for executed query */
List *stmts; /* list of PlannedStmts */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
@@ -227,7 +229,7 @@ extern Portal GetPortalByName(const char *name);
extern void PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan);
extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
diff --git a/src/include/utils/querycompletion.h b/src/include/utils/querycompletion.h
new file mode 100644
index 0000000000..59509a7c74
--- /dev/null
+++ b/src/include/utils/querycompletion.h
@@ -0,0 +1,46 @@
+/*-------------------------------------------------------------------------
+ *
+ * queryenvironment.h
+ * Access to functions to mutate the query environment and retrieve the
+ * actual data related to entries (if any).
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/queryenvironment.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef QUERYCOMPLETION_H
+#define QUERYCOMPLETION_H
+
+#include "utils/commandtag.h"
+
+typedef struct QueryCompletionData
+{
+ CommandTag commandTag;
+ uint64 nprocessed;
+} QueryCompletionData;
+
+static inline void
+InitializeQC(QueryCompletionData *qc)
+{
+ qc->commandTag = COMMANDTAG_UNKNOWN;
+ qc->nprocessed = 0;
+}
+
+static inline void
+SetQC(QueryCompletionData *qc, CommandTag commandTag, uint64 nprocessed)
+{
+ qc->commandTag = commandTag;
+ qc->nprocessed = nprocessed;
+}
+
+static inline void
+CopyQC(QueryCompletionData *dst, const QueryCompletionData *src)
+{
+ dst->commandTag = src->commandTag;
+ dst->nprocessed = src->nprocessed;
+}
+
+#endif /* QUERYCOMPLETION_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 5acf604f63..8ac2b67417 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -1473,7 +1473,7 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
case PLPGSQL_PROMISE_TG_TAG:
if (estate->evtrigdata == NULL)
elog(ERROR, "event trigger promise is not in an event trigger function");
- assign_text_var(estate, var, estate->evtrigdata->tag);
+ assign_text_var(estate, var, GetCommandTagName(estate->evtrigdata->tag));
break;
default:
@@ -4115,10 +4115,9 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
* tree(s), since those are the result of rewriting and could have
* been transmogrified into something else entirely.
*/
- if (plansource->commandTag &&
- (strcmp(plansource->commandTag, "INSERT") == 0 ||
- strcmp(plansource->commandTag, "UPDATE") == 0 ||
- strcmp(plansource->commandTag, "DELETE") == 0))
+ if (plansource->commandTag == COMMANDTAG_INSERT ||
+ plansource->commandTag == COMMANDTAG_UPDATE ||
+ plansource->commandTag == COMMANDTAG_DELETE)
{
stmt->mod_stmt = true;
break;
diff --git a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
index e1629ec618..b7bdb88ce7 100644
--- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
+++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
@@ -74,7 +74,7 @@ get_command_tag(PG_FUNCTION_ARGS)
if (!cmd->parsetree)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(cstring_to_text(CreateCommandTag(cmd->parsetree)));
+ PG_RETURN_TEXT_P(cstring_to_text(CreateCommandName(cmd->parsetree)));
}
/*
--
2.21.1 (Apple Git-122.3)
On 2020-Feb-07, Mark Dilger wrote:
Andres,
The previous patch set seemed to cause confusion, having separated
changes into multiple files. The latest patch, heavily influenced by
your review, is all in one file, attached.
Cool stuff.
I think is a little confused about what is source and what is generated.
I'm not clear why commandtag.c is a C file at all; wouldn't it be
simpler to have it as a Data::Dumper file or some sort of Perl struct,
so that it can be read easily by the Perl file? Similar to the
src/include/catalog/pg_*.dat files. That script can then generate all
the needed .c and .h files, which are not going to be part of the source
tree, and will always be in-sync and won't have the formatting
strictness about it. And you won't have the Martian syntax you had to
use in the commandtag.c file.
As for API, please don't shorten things such as SetQC, just use
SetQueryCompletion. Perhaps call it SetCompletionTag or SetCommandTag?.
(I'm not sure about the name "QueryCompletionData"; maybe CommandTag or
QueryTag would work better for that struct. There seems to be a lot of
effort in separating QueryCompletion from CommandTag; is that really
necessary?) Lastly, we have a convention that we have a struct called
FooData, and a typedef FooData *Foo, then use the latter in the API.
We don't adhere to that 100%, and some people dislike it, but I'd rather
be consistent and not be passing "FooData *" around; it's just noisier.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Feb 11, 2020, at 11:09 AM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
On 2020-Feb-07, Mark Dilger wrote:
Andres,
The previous patch set seemed to cause confusion, having separated
changes into multiple files. The latest patch, heavily influenced by
your review, is all in one file, attached.Cool stuff.
Thanks for the review!
I think is a little confused about what is source and what is generated.
The perl file generates nothing. It merely checks that the .h and .c files are valid and consistent with each other.
I'm not clear why commandtag.c is a C file at all; wouldn't it be
simpler to have it as a Data::Dumper file or some sort of Perl struct,
so that it can be read easily by the Perl file? Similar to the
src/include/catalog/pg_*.dat files. That script can then generate all
the needed .c and .h files, which are not going to be part of the source
tree, and will always be in-sync and won't have the formatting
strictness about it. And you won't have the Martian syntax you had to
use in the commandtag.c file.
I thought about generating the files rather than merely checking them. I could see arguments both ways. I wasn’t sure whether there would be broad support for having yet another perl script generating source files, nor for the maintenance burden of having to do that on all platforms. Having a perl script that merely sanity checks the source files has the advantage that there is no requirement for it to function on all platforms. There’s not even a requirement for it to be committed to the tree, since you could also argue that the maintenance burden of the script outweighs the burden of getting the source files right by hand.
As for API, please don't shorten things such as SetQC, just use
SetQueryCompletion. Perhaps call it SetCompletionTag or SetCommandTag?.
(I'm not sure about the name "QueryCompletionData"; maybe CommandTag or
QueryTag would work better for that struct.
I am happy to rename it as SetQueryCompletion.
There seems to be a lot of
effort in separating QueryCompletion from CommandTag; is that really
necessary?)
For some code paths, prior to this patch, the commandTag gets changed before returning, and I’m not just talking about the change where the rowcount gets written into the commandTag string. I have a work-in-progress patch to provide system views to track the number of commands executed of each type, and that patch includes the ability to distinguish between what the command started as and what it completed as, so I do want to keep those concepts separate. I rejected the “SetCommandTag” naming suggestion above because we’re really setting information about the completion (what it finished as) and not the command (what it started as). I rejected the “SetCompletionTag” naming because it’s not just the tag that is being set, but both the tag and the row count. I am happy with “SetQueryCompletion” because that naming is consistent with setting the pair of values.
Lastly, we have a convention that we have a struct called
FooData, and a typedef FooData *Foo, then use the latter in the API.
We don't adhere to that 100%, and some people dislike it, but I'd rather
be consistent and not be passing "FooData *" around; it's just noisier.
I’m familiar with the convention, and don’t like it, so I’ll have to look at a better way of naming this. I specifically don’t like it because it makes a mess of using the const qualifier.
Once again, thanks for the review! I will work to get another version of this patch posted around the time I post (separately) the command stats patch.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 2020-Feb-11, Mark Dilger wrote:
I thought about generating the files rather than merely checking them.
I could see arguments both ways. I wasn’t sure whether there would be
broad support for having yet another perl script generating source
files, nor for the maintenance burden of having to do that on all
platforms. Having a perl script that merely sanity checks the source
files has the advantage that there is no requirement for it to
function on all platforms. There’s not even a requirement for it to
be committed to the tree, since you could also argue that the
maintenance burden of the script outweighs the burden of getting the
source files right by hand.
No thanks.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Feb 11, 2020, at 12:50 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
On 2020-Feb-11, Mark Dilger wrote:
I thought about generating the files rather than merely checking them.
I could see arguments both ways. I wasn’t sure whether there would be
broad support for having yet another perl script generating source
files, nor for the maintenance burden of having to do that on all
platforms. Having a perl script that merely sanity checks the source
files has the advantage that there is no requirement for it to
function on all platforms. There’s not even a requirement for it to
be committed to the tree, since you could also argue that the
maintenance burden of the script outweighs the burden of getting the
source files right by hand.No thanks.
I’m not sure which option you are voting for:
(Option #1) Have the perl script generate the .c and .h file from a .dat file
(Option #2) Have the perl script validate but not generate the .c and .h files
(Option #3) Have no perl script, with all burden on the programmer to get the .c and .h files right by hand.
I think you’re voting against #3, and I’m guessing you’re voting for #1, but I’m not certain.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 2020-Feb-11, Mark Dilger wrote:
No thanks.
I’m not sure which option you are voting for:
(Option #1) Have the perl script generate the .c and .h file from a .dat file
(Option #2) Have the perl script validate but not generate the .c and .h files
(Option #3) Have no perl script, with all burden on the programmer to get the .c and .h files right by hand.
I think you’re voting against #3, and I’m guessing you’re voting for #1, but I’m not certain.
I was voting against #2 (burden the programmer with consistency checks
that must be fixed by hand, without actually doing the programmatically-
doable work), but I don't like #3 either. I do like #1.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Feb 11, 2020, at 1:02 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
On 2020-Feb-11, Mark Dilger wrote:
No thanks.
I’m not sure which option you are voting for:
(Option #1) Have the perl script generate the .c and .h file from a .dat file
(Option #2) Have the perl script validate but not generate the .c and .h files
(Option #3) Have no perl script, with all burden on the programmer to get the .c and .h files right by hand.
I think you’re voting against #3, and I’m guessing you’re voting for #1, but I’m not certain.
I was voting against #2 (burden the programmer with consistency checks
that must be fixed by hand, without actually doing the programmatically-
doable work), but I don't like #3 either. I do like #1.
Option #1 works for me. If I don’t see any contrary votes before I get back to this patch, I’ll implement it that way for the next version.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Feb 11, 2020, at 1:05 PM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
On Feb 11, 2020, at 1:02 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
On 2020-Feb-11, Mark Dilger wrote:
No thanks.
I’m not sure which option you are voting for:
(Option #1) Have the perl script generate the .c and .h file from a .dat file
(Option #2) Have the perl script validate but not generate the .c and .h files
(Option #3) Have no perl script, with all burden on the programmer to get the .c and .h files right by hand.
I think you’re voting against #3, and I’m guessing you’re voting for #1, but I’m not certain.
I was voting against #2 (burden the programmer with consistency checks
that must be fixed by hand, without actually doing the programmatically-
doable work), but I don't like #3 either. I do like #1.Option #1 works for me. If I don’t see any contrary votes before I get back to this patch, I’ll implement it that way for the next version.
While investigating how best to implement option #1, I took a look at how Catalog.pm does it.
Catalog.pm reads data files and eval()s chunks of them to vivify perl data.
# We're treating the input line as a piece of Perl, so we
# need to use string eval here. Tell perlcritic we know what
# we're doing.
eval '$hash_ref = ' . $_; ## no critic (ProhibitStringyEval)
This would only make sense to me if the string held in $_ had already been checked for safety, but Catalog.pm does very little to verify that the string is safe to eval. The assumption here, so far as I can infer, is that we don’t embed anything dangerous in our .dat files, so this should be ok. That may be true for the moment, but I can imagine a day when we start embedding perl functions as quoted text inside a data file, or shell commands as text which look enough like perl for eval() to be able to execute them. Developers who edit these .dat files and mess up the quoting, and then rerun ‘make’ to get the new .c and .h files generated, may not like the side effects. Perhaps I’m being overly paranoid….
Rather than add more code generation logic based on the design of Catalog.pm, I wrote a perl based data file parser that parses .dat files and returns vivified perl data, as Catalog.pm does, but with much stricter parsing logic to make certain nothing dangerous gets eval()ed. I put the new module in DataFile.pm. The commandtag data has been consolidated into a single .dat file. A new perl script, gencommandtag.pl, parses the commandtag.dat file and generates the .c and .h files. So far, only gencommandtag.pl uses DataFile.pm, but I’ve checked that it can parse all the .dat files currently in the source tree.
The new parser is more flexible about the structure of the data, which seems good to me for making it easier to add or modify data files in the future. The new parser does not yet have a means of hacking up the data to add autogenerated fields and such that Catalog.pm does, but I think a more clean break between parsing and autovivifying fields would be good anyway. If I get generally favorable reviews of DataFile.pm, I might go refactor Catalog.pm. For now, I’m just leaving Catalog.pm alone.
That script can then generate all
the needed .c and .h files, which are not going to be part of the source
tree, and will always be in-sync and won't have the formatting
strictness about it. And you won't have the Martian syntax you had to
use in the commandtag.c file.
I still have the “Martian syntax”, though now it is generated by the perl script. I can get rid of it, but I think Andres liked the Martian stuff.
We don't adhere to that 100%, and some people dislike it, but I'd rather
be consistent and not be passing "FooData *" around; it's just noisier.
I renamed QueryCompletionData to just QueryCompletion, and I’m passing pointers to that.
Attachments:
v4-0001-Migrating-commandTag-from-string-to-enum.patchapplication/octet-stream; name=v4-0001-Migrating-commandTag-from-string-to-enum.patch; x-unix-mode=0644Download
From 25e8872ff50a6e076159bcf36112ca032c556706 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Thu, 6 Feb 2020 11:41:44 -0800
Subject: [PATCH v4] Migrating commandTag from string to enum.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The backend was using strings to represent command tags and doing string
comparisons in multiple places. Fixing that by creating a new
CommandTag enum and using it instead.
Replacing numerous occurrences of char *completionTag with a
QueryCompletionData struct so that the code no longer stores information
about completed queries in a cstring. Only at the last moment, in
EndCommand(), does this get converted to a string.
EventTriggerCacheItem no longer holds an array of palloc’d tag strings
in sorted order, but rather just a Bitmapset over the CommandTags.
---
.../pg_stat_statements/pg_stat_statements.c | 18 +-
contrib/sepgsql/hooks.c | 6 +-
src/backend/catalog/DataFile.pm | 400 ++++++++
src/backend/commands/createas.c | 14 +-
src/backend/commands/event_trigger.c | 160 +--
src/backend/commands/matview.c | 2 +-
src/backend/commands/portalcmds.c | 16 +-
src/backend/commands/prepare.c | 4 +-
src/backend/executor/execMain.c | 4 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/spi.c | 22 +-
src/backend/replication/walsender.c | 18 +-
src/backend/tcop/dest.c | 37 +-
src/backend/tcop/postgres.c | 24 +-
src/backend/tcop/pquery.c | 111 +--
src/backend/tcop/utility.c | 558 +++++------
src/backend/utils/.gitignore | 3 +
src/backend/utils/Makefile | 26 +-
src/backend/utils/cache/evtcache.c | 27 +-
src/backend/utils/cache/plancache.c | 4 +-
src/backend/utils/commandtag.c | 119 +++
src/backend/utils/gencommandtag.pl | 285 ++++++
src/backend/utils/misc/.gitignore | 1 +
src/backend/utils/misc/Makefile | 4 +
src/backend/utils/mmgr/portalmem.c | 6 +-
src/include/Makefile | 4 +-
src/include/commands/createas.h | 3 +-
src/include/commands/event_trigger.h | 2 +-
src/include/commands/matview.h | 2 +-
src/include/commands/portalcmds.h | 3 +-
src/include/commands/prepare.h | 2 +-
src/include/nodes/parsenodes.h | 1 +
src/include/tcop/dest.h | 7 +-
src/include/tcop/pquery.h | 2 +-
src/include/tcop/utility.h | 15 +-
src/include/utils/.gitignore | 2 +
src/include/utils/commandtag.dat | 938 ++++++++++++++++++
src/include/utils/commandtag.h | 60 ++
src/include/utils/evtcache.h | 3 +-
src/include/utils/plancache.h | 7 +-
src/include/utils/portal.h | 6 +-
src/pl/plpgsql/src/pl_exec.c | 10 +-
.../test_ddl_deparse/test_ddl_deparse.c | 2 +-
src/tools/msvc/Solution.pm | 12 +
44 files changed, 2340 insertions(+), 614 deletions(-)
create mode 100644 src/backend/catalog/DataFile.pm
create mode 100644 src/backend/utils/commandtag.c
create mode 100755 src/backend/utils/gencommandtag.pl
create mode 100644 src/include/utils/commandtag.dat
create mode 100644 src/include/utils/commandtag.h
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index e0dbeebde3..1f3e9c1041 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -307,7 +307,7 @@ static void pgss_ExecutorEnd(QueryDesc *queryDesc);
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
static uint64 pgss_hash_string(const char *str, int len);
static void pgss_store(const char *query, uint64 queryId,
int query_location, int query_len,
@@ -960,7 +960,7 @@ static void
pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context,
ParamListInfo params, QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
@@ -998,11 +998,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
@@ -1013,10 +1013,8 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
INSTR_TIME_SET_CURRENT(duration);
INSTR_TIME_SUBTRACT(duration, start);
- /* parse command tag to retrieve the number of affected rows. */
- if (completionTag &&
- strncmp(completionTag, "COPY ", 5) == 0)
- rows = pg_strtouint64(completionTag + 5, NULL, 10);
+ if (qc && qc->commandTag == COMMANDTAG_COPY)
+ rows = qc->nprocessed;
else
rows = 0;
@@ -1060,11 +1058,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
}
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 997a64c87e..853b5b04ab 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -317,7 +317,7 @@ sepgsql_utility_command(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
@@ -380,11 +380,11 @@ sepgsql_utility_command(PlannedStmt *pstmt,
if (next_ProcessUtility_hook)
(*next_ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
diff --git a/src/backend/catalog/DataFile.pm b/src/backend/catalog/DataFile.pm
new file mode 100644
index 0000000000..f7bf7656fc
--- /dev/null
+++ b/src/backend/catalog/DataFile.pm
@@ -0,0 +1,400 @@
+#!/usr/bin/perl -w
+#----------------------------------------------------------------------
+# DataFile.pm
+# Perl module that safely converts data files into perl data.
+#
+# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/backend/catalog/DataFile.pm
+#
+#----------------------------------------------------------------------
+
+
+=pod
+
+=head1 NAME
+
+DataFile - module for safely parsing and vivifying perl data files
+
+=head1 SYNOPSIS
+
+ use DataFile;
+
+ my $array_reference = DataFile::ParseData("myfile.dat");
+
+=head1 DESCRIPTION
+
+DataFile contains a parsing function, ParseData, for parsing various
+data files. By convention, these data files are stored as a single array
+of hashes of key/value pairs.
+
+=pod
+
+=head1 METHODS
+
+=cut
+
+package DataFile;
+
+use strict;
+use warnings;
+use Config;
+
+=pod
+
+=over
+
+=item slurp_file(filename)
+
+Return the full contents of the specified file.
+
+=cut
+
+sub slurp_file
+{
+ my ($filename) = @_;
+ local $/;
+ my $contents;
+ if ($Config{osname} ne 'MSWin32')
+ {
+ open(my $in, '<', $filename)
+ or die "could not read \"$filename\": $!";
+ $contents = <$in>;
+ close $in;
+ }
+ else
+ {
+ my $fHandle = createFile($filename, "r", "rwd")
+ or die "could not open \"$filename\": $^E";
+ OsFHandleOpen(my $fh = IO::Handle->new(), $fHandle, 'r')
+ or die "could not read \"$filename\": $^E\n";
+ $contents = <$fh>;
+ CloseHandle($fHandle)
+ or die "could not close \"$filename\": $^E\n";
+ }
+ $contents =~ s/\r//g if $Config{osname} eq 'msys';
+ return $contents;
+}
+
+=pod
+
+=item ParseData(filename)
+
+Parse a file which represents a valid perl array reference of hash references,
+vivify the perl array reference, and return it.
+
+This function is paranoid about evaluation or interpolation of embedded perl
+and will die with an error message if the file contains anything that looks
+like it will result in code being executed.
+
+Mistakes in the data file making it an invalid perl object will draw errors
+about the line number in the file where parsing failed and an attempt to
+indicate what parsing problem was encountered there.
+
+=cut
+
+our (%looking, $pos);
+sub update_search($$)
+{
+ my ($lookingfor, $atpos) = @_;
+ if ($atpos > $pos)
+ {
+ %looking = ($lookingfor => 1);
+ $pos = $atpos;
+ }
+ elsif ($atpos == $pos)
+ {
+ $looking{$lookingfor} = 1;
+ }
+}
+
+sub ParseData
+{
+ my ($inputfile) = @_;
+
+ # Vivify the input data, but be paranoid about eval'ing arbitrary code
+ my $data;
+ eval {
+ my $untrusted = slurp_file($inputfile);
+ our $noeval = undef;
+ our $noevalstr = undef;
+ our $noevalpos = -1;
+ %looking = ();
+ $pos = -1;
+
+ # A quotable string is one that does not embed an unescaped end-quote
+ # or end with a backslash which would escape the quote that otherwise
+ # terminates the quoted string.
+ my $singlequotablere = qr/
+ # Verify through look-behind that we're at the
+ # beginning of a single-quoted string
+ (?<=')
+ # Consume the single-quoted string contents
+ # without backtracking or givebacks.
+ (?>
+ # Any single character except single-quote
+ # or backslash
+ [^'\\]
+ |
+ # Any escaped character, including escaped
+ # single-quotes and escaped backslashes
+ \\.
+ )*+
+ # Verify through look-ahead that we're at the
+ # end of a single-quoted string
+ (?=')
+ /msx;
+
+ # Inside double-quoted strings, Perl interpolates substrings which look
+ # like variables with those variables' values. This seems a bit unsafe
+ # to allow in this package, so we check double-quoted strings for
+ # anything that Perl might interpret this way at 'eval' time. Such
+ # substrings still match our pattern, but we set a flag alerting us to
+ # not proceed to interpolate the substring.
+ my $doublequotablere = qr/
+ # Verify through look-behind that we're at the
+ # beginning of a double-quoted string
+ (?<=")
+ # Begin capturing. If we encounter an illegal
+ # sequence, we can save the captured string for
+ # use later during error reporting
+ (
+ # Consume the double-quoted string contents
+ # without backtracking or givebacks.
+ (?>
+ # Any character except double-quote,
+ # backslash, or variable indicators [$%@]
+ [^"\\%@\$]
+ |
+ # Any escaped character
+ \\.
+ |
+ # A variable indicator, but we set the flag
+ # that we must not eval this string
+ [%@\$](?{ local $noeval = 1; })
+ )*+
+ # verify through look-ahead that we're at the
+ # end of the double-quoted string
+ (?=")
+ # End capture
+ )
+ # If we encountered a disallowed character
+ # during the capture, record the entire captured
+ # string for later
+ (?{ $noevalstr = $^N, $noevalpos = pos()
+ if ($noeval && !defined $noevalstr);
+ })
+ /msx;
+
+ my $quotedstringre = qr/
+ (?:
+ '
+ (?{ update_search("end of single quoted string", pos()) })
+ $singlequotablere
+ '
+ |
+ "
+ (?{ update_search("end of double quoted string", pos()) })
+ $doublequotablere
+ "
+ )
+ /msx;
+
+ # A perl comment cannot occur inside a quoted string, but otherwise it can
+ # occur pretty much anywhere. So long as we're not inside a quotation, we
+ # can match a # to the end of the line
+ my $commentre = qr/
+ (?:
+ (?{ update_search("comment", pos()) })
+ # Beginning of a comment
+ \#
+ # Anything other than end-of-line, no backtracking,
+ # and no give-backs
+ (?>[^\r\n])*+
+ # end-of-line
+ \r?\n
+ )
+ /msx;
+
+ # Comments and/or whitespace can occur just about anywhere. It is convenient
+ # to have a single regex that matches them so we don't have to think about
+ # comments much from here onward.
+ my $spacere = qr/
+ (?:
+ # any number of comment lines, including none, including
+ # leading whitespace before the comment begins
+ (?:
+ (?{ update_search("whitespace", pos()) })
+ (?>\s)*+ # whitespace without backtracking
+ (?{ update_search("comment", pos()) })
+ $commentre
+ )*+
+ # whitespace following the zero or more comment lines,
+ # without backtracking
+ (?{ update_search("whitespace", pos()) })
+ (?>\s)*+
+ )
+ /msx;
+
+ # Keys must be either quoted strings, numbers, or barewords if followed
+ # by an arrow operator. The arrow operator is not part of the key, however,
+ # so we check that with a look-ahead assertion
+ my $keyre = qr/
+ (?:
+ $quotedstringre
+ |
+ \d+
+ |
+ # bareword only allowed before arrow op, but there cannot
+ # be comments inbetween, only whitespace
+ \w+(?=\s*=>)
+ )
+ /msx;
+ # Values must be either numbers or quoted strings
+ my $valuere = qr/
+ (?:
+ $quotedstringre
+ |
+ \d+
+ )
+ /msx;
+
+ # Key/Value pairs can use arrow or comma separators. We hid the complexity
+ # of arrow operator quoting in the keyre, above
+ my $keyvaluere = qr/
+ (?{ update_search("key value pair", pos()) })
+ (?:
+ $keyre
+ $spacere
+ (?:
+ =>
+ |
+ ,
+ )
+ $spacere
+ $valuere
+ )
+ /msx;
+
+ # Key/Value pair lists
+ my $keyvaluelistre = qr/
+ # Optional key-value pair
+ (?:
+ $keyvaluere
+ $spacere
+
+ # Optionally followed by more
+ (?:
+ , # Required
+ $spacere
+ $keyvaluere
+ )*
+ # Optionally ending with a comma
+ (?:
+ $spacere
+ ,
+ )?
+ )?
+ /msx;
+
+ # Hash reference comprised of key/value pairs
+ my $hashrefre = qr/
+ (?:
+ (?{ update_search("beginning of hash reference", pos()) })
+ \{ # Open hash
+ $spacere
+ $keyvaluelistre
+ $spacere
+ (?{ update_search("ending of hash reference", pos()) })
+ \} # Close hash
+ )
+ /msx;
+
+ # List of hash references
+ my $hashreflist = qr/
+ # Optional hash ref
+ (?:
+ $hashrefre
+ $spacere
+
+ # Optionally followed by more
+ (?:
+ , # Required
+ $spacere
+ $hashrefre
+ )*+
+ # Optionally ending with a comma
+ (?:
+ $spacere
+ ,
+ )?+
+ )?+
+ /msx;
+
+ # Reference to list of hash references
+ my $aryofhashrefre = qr/
+ (?:
+ (?{ update_search("beginning of array reference", pos()) })
+ \[ # Open array
+ $spacere
+ $hashreflist
+ $spacere
+ (?{ update_search("ending of array reference", pos()) })
+ \] # Close array
+ )
+ /msx;
+
+
+ # After removing all comments, remaining structure must strictly
+ # match an array reference of hash references of key/value pairs
+ # with no code that perl's eval() could be tricked into running.
+ #
+ # This parser is stricter than necessary, but it should allow all
+ # data to be specified that we need it to allow, so instead of
+ # making this parser more complicated (or permissive), see if you
+ # can make the data file follow the expected format.
+ #
+ if ($untrusted =~ m/^
+ $spacere
+ $aryofhashrefre
+ $spacere
+ $/msx)
+ {
+ # If we encountered anything that might cause eval() to perform
+ # variable interpolation, complain and do not eval(). This may
+ # be overly paranoid....
+ if (defined $noevalstr)
+ {
+ my $lineno = scalar(split/\n/, substr($untrusted, 0, $noevalpos));
+ die "\nPossible attempt at variable interpolation in double " .
+ "quoted string in $inputfile, line $lineno: \"$noevalstr\"";
+ }
+
+ # We're treating the input file as a piece of Perl, so we
+ # need to use string eval here. Tell perlcritic we know what
+ # we're doing.
+ #
+ $data = eval $untrusted; ## no critic (ProhibitStringyEval)
+ die $@ if $@;
+ }
+ else
+ {
+ my $lineno = scalar(split/\n/, substr($untrusted, 0, $pos));
+ my $looking = join(" or ", keys %looking);
+ die("\n$inputfile does not match our expected format;\n" .
+ "At line $lineno, looking for $looking\n");
+ }
+ };
+ die $@ if $@;
+
+ return $data;
+}
+
+=pod
+
+=back
+
+=cut
+
+1;
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cc02cf824e..3bcd7a95af 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -10,7 +10,7 @@
*
* Formerly, CTAS was implemented as a variant of SELECT, which led
* to assorted legacy behaviors that we still try to preserve, notably that
- * we must return a tuples-processed count in the completionTag. (We no
+ * we must return a tuples-processed count in the QueryCompletion. (We no
* longer do that for CTAS ... WITH NO DATA, however.)
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
@@ -225,7 +225,7 @@ create_ctas_nodata(List *tlist, IntoClause *into)
ObjectAddress
ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ParamListInfo params, QueryEnvironment *queryEnv,
- char *completionTag)
+ QueryCompletion *qc)
{
Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
@@ -270,7 +270,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
Assert(!is_matview); /* excluded by syntax */
- ExecuteQuery(pstate, estmt, into, params, dest, completionTag);
+ ExecuteQuery(pstate, estmt, into, params, dest, qc);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
@@ -352,11 +352,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* run the plan to completion */
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
- /* save the rowcount if we're given a completionTag to fill */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ /* save the rowcount if we're given a qc to fill */
+ if (qc)
+ SetQueryCompletion(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 71911d4067..7c8415475f 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -78,59 +78,6 @@ typedef struct
bool supported;
} event_trigger_support_data;
-typedef enum
-{
- EVENT_TRIGGER_COMMAND_TAG_OK,
- EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED,
- EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED
-} event_trigger_command_tag_check_result;
-
-/* XXX merge this with ObjectTypeMap? */
-static const event_trigger_support_data event_trigger_support[] = {
- {"ACCESS METHOD", true},
- {"AGGREGATE", true},
- {"CAST", true},
- {"CONSTRAINT", true},
- {"COLLATION", true},
- {"CONVERSION", true},
- {"DATABASE", false},
- {"DOMAIN", true},
- {"EXTENSION", true},
- {"EVENT TRIGGER", false},
- {"FOREIGN DATA WRAPPER", true},
- {"FOREIGN TABLE", true},
- {"FUNCTION", true},
- {"INDEX", true},
- {"LANGUAGE", true},
- {"MATERIALIZED VIEW", true},
- {"OPERATOR", true},
- {"OPERATOR CLASS", true},
- {"OPERATOR FAMILY", true},
- {"POLICY", true},
- {"PROCEDURE", true},
- {"PUBLICATION", true},
- {"ROLE", false},
- {"ROUTINE", true},
- {"RULE", true},
- {"SCHEMA", true},
- {"SEQUENCE", true},
- {"SERVER", true},
- {"STATISTICS", true},
- {"SUBSCRIPTION", true},
- {"TABLE", true},
- {"TABLESPACE", false},
- {"TRANSFORM", true},
- {"TRIGGER", true},
- {"TEXT SEARCH CONFIGURATION", true},
- {"TEXT SEARCH DICTIONARY", true},
- {"TEXT SEARCH PARSER", true},
- {"TEXT SEARCH TEMPLATE", true},
- {"TYPE", true},
- {"USER MAPPING", true},
- {"VIEW", true},
- {NULL, false}
-};
-
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -150,8 +97,6 @@ typedef struct SQLDropObject
static void AlterEventTriggerOwner_internal(Relation rel,
HeapTuple tup,
Oid newOwnerId);
-static event_trigger_command_tag_check_result check_ddl_tag(const char *tag);
-static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(const char *tag);
static void error_duplicate_filter_variable(const char *defname);
static Datum filter_list_to_array(List *filterlist);
static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname,
@@ -259,71 +204,23 @@ validate_ddl_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
- event_trigger_command_tag_check_result result;
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
- result = check_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED)
+ if (commandTag == COMMANDTAG_UNKNOWN)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
- tag, filtervar)));
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
+ tagstr, filtervar)));
+ if ( ! command_tag_event_trigger_ok(commandTag))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
-static event_trigger_command_tag_check_result
-check_ddl_tag(const char *tag)
-{
- const char *obtypename;
- const event_trigger_support_data *etsd;
-
- /*
- * Handle some idiosyncratic special cases.
- */
- if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
- pg_strcasecmp(tag, "SELECT INTO") == 0 ||
- pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
- pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
- pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
- pg_strcasecmp(tag, "COMMENT") == 0 ||
- pg_strcasecmp(tag, "GRANT") == 0 ||
- pg_strcasecmp(tag, "REVOKE") == 0 ||
- pg_strcasecmp(tag, "DROP OWNED") == 0 ||
- pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
- /*
- * Otherwise, command should be CREATE, ALTER, or DROP.
- */
- if (pg_strncasecmp(tag, "CREATE ", 7) == 0)
- obtypename = tag + 7;
- else if (pg_strncasecmp(tag, "ALTER ", 6) == 0)
- obtypename = tag + 6;
- else if (pg_strncasecmp(tag, "DROP ", 5) == 0)
- obtypename = tag + 5;
- else
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
-
- /*
- * ...and the object type should be something recognizable.
- */
- for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++)
- if (pg_strcasecmp(etsd->obtypename, obtypename) == 0)
- break;
- if (etsd->obtypename == NULL)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
- if (!etsd->supported)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-}
-
/*
* Validate DDL command tags for event table_rewrite.
*/
@@ -334,29 +231,18 @@ validate_table_rewrite_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
- event_trigger_command_tag_check_result result;
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
- result = check_table_rewrite_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
+ if (! command_tag_table_rewrite_ok(commandTag))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
-static event_trigger_command_tag_check_result
-check_table_rewrite_ddl_tag(const char *tag)
-{
- if (pg_strcasecmp(tag, "ALTER TABLE") == 0 ||
- pg_strcasecmp(tag, "ALTER TYPE") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
-}
-
/*
* Complain about a duplicate filter variable.
*/
@@ -663,7 +549,7 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
* tags matching.
*/
static bool
-filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
+filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
{
/*
* Filter by session replication role, knowing that we never see disabled
@@ -681,9 +567,7 @@ filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
}
/* Filter by tags, if any were specified. */
- if (item->ntags != 0 && bsearch(tag, item->tag,
- item->ntags, sizeof(char *),
- pg_qsort_strcmp) == NULL)
+ if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
return false;
/* if we reach that point, we're not filtering out this item */
@@ -700,7 +584,7 @@ EventTriggerCommonSetup(Node *parsetree,
EventTriggerEvent event, const char *eventstr,
EventTriggerData *trigdata)
{
- const char *tag;
+ CommandTag tag;
List *cachelist;
ListCell *lc;
List *runlist = NIL;
@@ -716,25 +600,25 @@ EventTriggerCommonSetup(Node *parsetree,
*
* If this cross-check fails for you, you probably need to either adjust
* standard_ProcessUtility() not to invoke event triggers for the command
- * type in question, or you need to adjust check_ddl_tag to accept the
+ * type in question, or you need to adjust event_trigger_ok to accept the
* relevant command tag.
*/
#ifdef USE_ASSERT_CHECKING
{
- const char *dbgtag;
+ CommandTag dbgtag;
dbgtag = CreateCommandTag(parsetree);
if (event == EVT_DDLCommandStart ||
event == EVT_DDLCommandEnd ||
event == EVT_SQLDrop)
{
- if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ if (! command_tag_event_trigger_ok(dbgtag))
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
else if (event == EVT_TableRewrite)
{
- if (check_table_rewrite_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ if (! command_tag_table_rewrite_ok(dbgtag))
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
}
#endif
@@ -758,7 +642,7 @@ EventTriggerCommonSetup(Node *parsetree,
{
EventTriggerCacheItem *item = lfirst(lc);
- if (filter_event_trigger(&tag, item))
+ if (filter_event_trigger(tag, item))
{
/* We must plan to fire this trigger. */
runlist = lappend_oid(runlist, item->fnoid);
@@ -2136,7 +2020,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
values[i++] = Int32GetDatum(addr.objectSubId);
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
/* object_type */
values[i++] = CStringGetTextDatum(type);
/* schema */
@@ -2161,7 +2045,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
nulls[i++] = true;
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
/* object_type */
values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
/* schema */
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 1ee37c1aeb..c3954f3e24 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -136,7 +136,7 @@ SetMatViewPopulatedState(Relation relation, bool newstate)
*/
ObjectAddress
ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag)
+ ParamListInfo params, QueryCompletion *qc)
{
Oid matviewOid;
Relation matviewRel;
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 7e5c805a1e..744d79a2e2 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -106,7 +106,8 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
PortalDefineQuery(portal,
NULL,
queryString,
- "SELECT", /* cursor's query is always a SELECT */
+ COMMANDTAG_SELECT, /* cursor's query is always a
+ * SELECT */
list_make1(plan),
NULL);
@@ -160,15 +161,14 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
*
* stmt: parsetree node for command
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store a command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*/
void
PerformPortalFetch(FetchStmt *stmt,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Portal portal;
uint64 nprocessed;
@@ -203,10 +203,8 @@ PerformPortalFetch(FetchStmt *stmt,
dest);
/* Return command status if wanted */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT,
- stmt->ismove ? "MOVE" : "FETCH",
- nprocessed);
+ if (qc)
+ SetQueryCompletion(qc, stmt->ismove ? COMMANDTAG_MOVE : COMMANDTAG_FETCH, nprocessed);
}
/*
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index c4e4b6eaec..f917fc9c7a 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -187,7 +187,7 @@ void
ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
PreparedStatement *entry;
CachedPlan *cplan;
@@ -288,7 +288,7 @@ ExecuteQuery(ParseState *pstate,
*/
PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
- (void) PortalRun(portal, count, false, true, dest, dest, completionTag);
+ (void) PortalRun(portal, count, false, true, dest, dest, qc);
PortalDrop(portal, false);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index ee5c3a60ff..751b36b6bf 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -787,11 +787,11 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
if (isTempNamespace(get_rel_namespace(rte->relid)))
continue;
- PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
+ PreventCommandTagIfReadOnly(CreateCommandTag((Node *) plannedstmt));
}
if (plannedstmt->commandType != CMD_SELECT || plannedstmt->hasModifyingCTE)
- PreventCommandIfParallelMode(CreateCommandTag((Node *) plannedstmt));
+ PreventCommandTagIfParallelMode(CreateCommandTag((Node *) plannedstmt));
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 5cff6c4321..9b45a8a9a0 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -530,7 +530,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a SQL function",
- CreateCommandTag(stmt->utilityStmt))));
+ CreateCommandName(stmt->utilityStmt))));
}
if (fcache->readonly_func && !CommandIsReadOnly(stmt))
@@ -538,7 +538,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ CreateCommandName((Node *) stmt))));
/* OK, build the execution_state for this query */
newes = (execution_state *) palloc(sizeof(execution_state));
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index c46764bf42..d0a73778ed 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1338,7 +1338,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
/* translator: %s is name of a SQL command, eg INSERT */
errmsg("cannot open %s query as cursor",
- plansource->commandTag)));
+ GetCommandTagName(plansource->commandTag))));
}
Assert(list_length(plan->plancache_list) == 1);
@@ -1469,7 +1469,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) pstmt))));
+ CreateCommandName((Node *) pstmt))));
}
}
@@ -2255,7 +2255,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ CreateCommandName((Node *) stmt))));
/*
* If not read-only mode, advance the command counter before each
@@ -2291,9 +2291,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else
{
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
ProcessUtilityContext context;
+ InitializeQueryCompletion(&qc);
+
/*
* If the SPI context is atomic, or we are asked to manage
* snapshots, then we are in an atomic execution context.
@@ -2312,7 +2314,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
paramLI,
_SPI_current->queryEnv,
dest,
- completionTag);
+ &qc);
/* Update "processed" if stmt returned tuples */
if (_SPI_current->tuptable)
@@ -2328,9 +2330,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
{
CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
- if (strncmp(completionTag, "SELECT ", 7) == 0)
- _SPI_current->processed =
- pg_strtouint64(completionTag + 7, NULL, 10);
+ if (qc.commandTag == COMMANDTAG_SELECT)
+ _SPI_current->processed = qc.nprocessed;
else
{
/*
@@ -2351,9 +2352,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else if (IsA(stmt->utilityStmt, CopyStmt))
{
- Assert(strncmp(completionTag, "COPY ", 5) == 0);
- _SPI_current->processed = pg_strtouint64(completionTag + 5,
- NULL, 10);
+ Assert(qc.commandTag == COMMANDTAG_COPY);
+ _SPI_current->processed = qc.nprocessed;
}
}
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index abb533b9d0..dc6c16101b 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1074,8 +1074,11 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
static void
DropReplicationSlot(DropReplicationSlotCmd *cmd)
{
+ QueryCompletion qc;
+
ReplicationSlotDrop(cmd->slotname, !cmd->wait);
- EndCommand("DROP_REPLICATION_SLOT", DestRemote);
+ SetQueryCompletion(&qc, COMMANDTAG_DROP_REPLICATION_SLOT, 0);
+ EndCommand(&qc, DestRemote, false);
}
/*
@@ -1086,6 +1089,7 @@ static void
StartLogicalReplication(StartReplicationCmd *cmd)
{
StringInfoData buf;
+ QueryCompletion qc;
/* make sure that our requirements are still fulfilled */
CheckLogicalDecodingRequirements();
@@ -1160,7 +1164,8 @@ StartLogicalReplication(StartReplicationCmd *cmd)
WalSndSetState(WALSNDSTATE_STARTUP);
/* Get out of COPY mode (CommandComplete). */
- EndCommand("COPY 0", DestRemote);
+ SetQueryCompletion(&qc, COMMANDTAG_COPY, 0);
+ EndCommand(&qc, DestRemote, false);
}
/*
@@ -1464,6 +1469,7 @@ exec_replication_command(const char *cmd_string)
Node *cmd_node;
MemoryContext cmd_context;
MemoryContext old_context;
+ QueryCompletion qc;
/*
* If WAL sender has been told that shutdown is getting close, switch its
@@ -1614,7 +1620,8 @@ exec_replication_command(const char *cmd_string)
MemoryContextDelete(cmd_context);
/* Send CommandComplete message */
- EndCommand("SELECT", DestRemote);
+ SetQueryCompletion(&qc, COMMANDTAG_SELECT, 0);
+ EndCommand(&qc, DestRemote, true);
/* Report to pgstat that this process is now idle */
pgstat_report_activity(STATE_IDLE, NULL);
@@ -2867,8 +2874,11 @@ WalSndDone(WalSndSendDataCallback send_data)
if (WalSndCaughtUp && sentPtr == replicatedPtr &&
!pq_is_send_pending())
{
+ QueryCompletion qc;
+
/* Inform the standby that XLOG streaming is done */
- EndCommand("COPY 0", DestRemote);
+ SetQueryCompletion(&qc, COMMANDTAG_COPY, 0);
+ EndCommand(&qc, DestRemote, false);
pq_flush();
proc_exit(0);
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 09c1dcbb53..324da752f4 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -100,7 +100,7 @@ DestReceiver *None_Receiver = (DestReceiver *) &donothingDR;
* ----------------
*/
void
-BeginCommand(const char *commandTag, CommandDest dest)
+BeginCommand(CommandTag commandTag, CommandDest dest)
{
/* Nothing to do at present */
}
@@ -163,8 +163,12 @@ CreateDestReceiver(CommandDest dest)
* ----------------
*/
void
-EndCommand(const char *commandTag, CommandDest dest)
+EndCommand(const QueryCompletion *qc, CommandDest dest, bool force_undecorated_output)
{
+ char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag tag;
+ const char *tagname;
+
switch (dest)
{
case DestRemote:
@@ -172,11 +176,32 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestRemoteSimple:
/*
- * We assume the commandTag is plain ASCII and therefore requires
- * no encoding conversion.
+ * We assume the tagname is plain ASCII and therefore requires no
+ * encoding conversion.
+ *
+ * We no longer display LastOid, but to preserve the wire protocol,
+ * we write InvalidOid where the LastOid used to be written. For
+ * efficiency in the snprintf(), hard-code InvalidOid as zero.
+ *
+ * All cases where LastOid was written also write nprocessed count,
+ * so just Assert that rather than having an extra test.
*/
- pq_putmessage('C', commandTag, strlen(commandTag) + 1);
- break;
+ tag = qc->commandTag;
+ tagname = GetCommandTagName(tag);
+
+ if (command_tag_display_last_oid(tag) && !force_undecorated_output)
+ {
+ Assert(InvalidOid == 0);
+ Assert(command_tag_display_rowcount(tag));
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "%s 0 " UINT64_FORMAT, tagname, qc->nprocessed);
+ }
+ else if (command_tag_display_rowcount(tag) && !force_undecorated_output)
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "%s " UINT64_FORMAT, tagname, qc->nprocessed);
+ else
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s", tagname);
+ pq_putmessage('C', completionTag, strlen(completionTag) + 1);
case DestNone:
case DestDebug:
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0a6f80963b..62c181b0a5 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1064,8 +1064,8 @@ exec_simple_query(const char *query_string)
{
RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);
bool snapshot_set = false;
- const char *commandTag;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag commandTag;
+ QueryCompletion qc;
MemoryContext per_parsetree_context = NULL;
List *querytree_list,
*plantree_list;
@@ -1081,7 +1081,7 @@ exec_simple_query(const char *query_string)
*/
commandTag = CreateCommandTag(parsetree->stmt);
- set_ps_display(commandTag, false);
+ set_ps_display(GetCommandTagName(commandTag), false);
BeginCommand(commandTag, dest);
@@ -1230,7 +1230,7 @@ exec_simple_query(const char *query_string)
true,
receiver,
receiver,
- completionTag);
+ &qc);
receiver->rDestroy(receiver);
@@ -1281,7 +1281,7 @@ exec_simple_query(const char *query_string)
* command the client sent, regardless of rewriting. (But a command
* aborted by error will not send an EndCommand report at all.)
*/
- EndCommand(completionTag, dest);
+ EndCommand(&qc, dest, false);
/* Now we may drop the per-parsetree context, if one was created. */
if (per_parsetree_context)
@@ -1343,7 +1343,7 @@ exec_parse_message(const char *query_string, /* string to execute */
MemoryContext oldcontext;
List *parsetree_list;
RawStmt *raw_parse_tree;
- const char *commandTag;
+ CommandTag commandTag;
List *querytree_list;
CachedPlanSource *psrc;
bool is_named;
@@ -1505,7 +1505,7 @@ exec_parse_message(const char *query_string, /* string to execute */
{
/* Empty input string. This is legal. */
raw_parse_tree = NULL;
- commandTag = NULL;
+ commandTag = COMMANDTAG_UNKNOWN;
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
querytree_list = NIL;
}
@@ -2022,7 +2022,7 @@ exec_execute_message(const char *portal_name, long max_rows)
DestReceiver *receiver;
Portal portal;
bool completed;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
const char *sourceText;
const char *prepStmtName;
ParamListInfo portalParams;
@@ -2049,7 +2049,7 @@ exec_execute_message(const char *portal_name, long max_rows)
* If the original query was a null string, just return
* EmptyQueryResponse.
*/
- if (portal->commandTag == NULL)
+ if (portal->commandTag == COMMANDTAG_UNKNOWN)
{
Assert(portal->stmts == NIL);
NullCommand(dest);
@@ -2095,7 +2095,7 @@ exec_execute_message(const char *portal_name, long max_rows)
pgstat_report_activity(STATE_RUNNING, sourceText);
- set_ps_display(portal->commandTag, false);
+ set_ps_display(GetCommandTagName(portal->commandTag), false);
if (save_log_statement_stats)
ResetUsage();
@@ -2176,7 +2176,7 @@ exec_execute_message(const char *portal_name, long max_rows)
!execute_is_fetch && max_rows == FETCH_ALL,
receiver,
receiver,
- completionTag);
+ &qc);
receiver->rDestroy(receiver);
@@ -2209,7 +2209,7 @@ exec_execute_message(const char *portal_name, long max_rows)
}
/* Send appropriate CommandComplete to client */
- EndCommand(completionTag, dest);
+ EndCommand(&qc, dest, false);
}
else
{
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 0f5801e046..f2089f5c34 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -40,7 +40,7 @@ static void ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletion *qc);
static void FillPortalStore(Portal portal, bool isTopLevel);
static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
DestReceiver *dest);
@@ -48,11 +48,11 @@ static uint64 PortalRunSelect(Portal portal, bool forward, long count,
DestReceiver *dest);
static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
static void PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletion *qc);
static uint64 DoPortalRunFetch(Portal portal,
FetchDirection fdirection,
long count,
@@ -125,10 +125,9 @@ FreeQueryDesc(QueryDesc *qdesc)
* sourceText: the source text of the query
* params: any parameters needed
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store the command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want a status string.
*
* Must be called in a memory context that will be reset or deleted on
* error; otherwise the executor's memory usage will be leaked.
@@ -139,7 +138,7 @@ ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
QueryDesc *queryDesc;
@@ -161,38 +160,26 @@ ProcessQuery(PlannedStmt *plan,
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
/*
- * Build command completion status string, if caller wants one.
+ * Build command completion status data, if caller wants one.
*/
- if (completionTag)
+ if (qc)
{
- Oid lastOid;
-
switch (queryDesc->operation)
{
case CMD_SELECT:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed);
break;
case CMD_INSERT:
- /* lastoid doesn't exist anymore */
- lastOid = InvalidOid;
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "INSERT %u " UINT64_FORMAT,
- lastOid, queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, COMMANDTAG_INSERT, queryDesc->estate->es_processed);
break;
case CMD_UPDATE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "UPDATE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, COMMANDTAG_UPDATE, queryDesc->estate->es_processed);
break;
case CMD_DELETE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "DELETE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, COMMANDTAG_DELETE, queryDesc->estate->es_processed);
break;
default:
- strcpy(completionTag, "???");
+ SetQueryCompletion(qc, COMMANDTAG_UNKNOWN, queryDesc->estate->es_processed);
break;
}
}
@@ -675,9 +662,8 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
*
* altdest: where to send output of non-primary queries
*
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
- * May be NULL if caller doesn't want a status string.
+ * qc: where to store command completion status data.
+ * May be NULL if caller doesn't want status data.
*
* Returns true if the portal's execution is complete, false if it was
* suspended due to exhaustion of the count parameter.
@@ -685,7 +671,7 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
bool
PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletion *qc)
{
bool result;
uint64 nprocessed;
@@ -700,9 +686,9 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
TRACE_POSTGRESQL_QUERY_EXECUTE_START();
- /* Initialize completion tag to empty string */
- if (completionTag)
- completionTag[0] = '\0';
+ /* Initialize empty completion data */
+ if (qc)
+ InitializeQueryCompletion(qc);
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
{
@@ -771,16 +757,12 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
/*
* If the portal result contains a command tag and the caller
- * gave us a pointer to store it, copy it. Patch the "SELECT"
- * tag to also provide the rowcount.
+ * gave us a pointer to store it, copy it and update the rowcount.
*/
- if (completionTag && portal->commandTag)
+ if (qc && portal->qc.commandTag != COMMANDTAG_UNKNOWN)
{
- if (strcmp(portal->commandTag, "SELECT") == 0)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT, nprocessed);
- else
- strcpy(completionTag, portal->commandTag);
+ CopyQueryCompletion(qc, &portal->qc);
+ qc->nprocessed = nprocessed;
}
/* Mark portal not active */
@@ -794,7 +776,7 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
case PORTAL_MULTI_QUERY:
PortalRunMulti(portal, isTopLevel, false,
- dest, altdest, completionTag);
+ dest, altdest, qc);
/* Prevent portal's commands from being re-executed */
MarkPortalDone(portal);
@@ -1005,8 +987,9 @@ static void
FillPortalStore(Portal portal, bool isTopLevel)
{
DestReceiver *treceiver;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
+ InitializeQueryCompletion(&qc);
PortalCreateHoldStore(portal);
treceiver = CreateDestReceiver(DestTuplestore);
SetTuplestoreDestReceiverParams(treceiver,
@@ -1014,8 +997,6 @@ FillPortalStore(Portal portal, bool isTopLevel)
portal->holdContext,
false);
- completionTag[0] = '\0';
-
switch (portal->strategy)
{
case PORTAL_ONE_RETURNING:
@@ -1028,12 +1009,12 @@ FillPortalStore(Portal portal, bool isTopLevel)
* portal's holdSnapshot to the snapshot used (or a copy of it).
*/
PortalRunMulti(portal, isTopLevel, true,
- treceiver, None_Receiver, completionTag);
+ treceiver, None_Receiver, &qc);
break;
case PORTAL_UTIL_SELECT:
PortalRunUtility(portal, linitial_node(PlannedStmt, portal->stmts),
- isTopLevel, true, treceiver, completionTag);
+ isTopLevel, true, treceiver, &qc);
break;
default:
@@ -1042,9 +1023,9 @@ FillPortalStore(Portal portal, bool isTopLevel)
break;
}
- /* Override default completion tag with actual command result */
- if (completionTag[0] != '\0')
- portal->commandTag = pstrdup(completionTag);
+ /* Override portal completion data with actual command results */
+ if (qc.commandTag != COMMANDTAG_UNKNOWN)
+ CopyQueryCompletion(&portal->qc, &qc);
treceiver->rDestroy(treceiver);
}
@@ -1130,7 +1111,7 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count,
static void
PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
Node *utilityStmt = pstmt->utilityStmt;
Snapshot snapshot;
@@ -1178,7 +1159,7 @@ PortalRunUtility(Portal portal, PlannedStmt *pstmt,
portal->portalParams,
portal->queryEnv,
dest,
- completionTag);
+ qc);
/* Some utility statements may change context on us */
MemoryContextSwitchTo(portal->portalContext);
@@ -1202,7 +1183,7 @@ static void
PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletion *qc)
{
bool active_snapshot_set = false;
ListCell *stmtlist_item;
@@ -1284,7 +1265,7 @@ PortalRunMulti(Portal portal,
portal->sourceText,
portal->portalParams,
portal->queryEnv,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1319,7 +1300,7 @@ PortalRunMulti(Portal portal,
Assert(!active_snapshot_set);
/* statement can set tag string */
PortalRunUtility(portal, pstmt, isTopLevel, false,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1350,8 +1331,8 @@ PortalRunMulti(Portal portal,
PopActiveSnapshot();
/*
- * If a command completion tag was supplied, use it. Otherwise use the
- * portal's commandTag as the default completion tag.
+ * If a query completion data was supplied, use it. Otherwise use the
+ * portal's query completion data.
*
* Exception: Clients expect INSERT/UPDATE/DELETE tags to have counts, so
* fake them with zeros. This can happen with DO INSTEAD rules if there
@@ -1361,18 +1342,12 @@ PortalRunMulti(Portal portal,
* e.g. an INSERT that does an UPDATE instead should not print "0 1" if
* one row was updated. See QueryRewrite(), step 3, for details.
*/
- if (completionTag && completionTag[0] == '\0')
+ if (qc && qc->commandTag == COMMANDTAG_UNKNOWN)
{
- if (portal->commandTag)
- strcpy(completionTag, portal->commandTag);
- if (strcmp(completionTag, "SELECT") == 0)
- sprintf(completionTag, "SELECT 0 0");
- else if (strcmp(completionTag, "INSERT") == 0)
- strcpy(completionTag, "INSERT 0 0");
- else if (strcmp(completionTag, "UPDATE") == 0)
- strcpy(completionTag, "UPDATE 0");
- else if (strcmp(completionTag, "DELETE") == 0)
- strcpy(completionTag, "DELETE 0");
+ if (portal->qc.commandTag != COMMANDTAG_UNKNOWN)
+ CopyQueryCompletion(qc, &portal->qc);
+ /* If the caller supplied a qc, we should have set it by now. */
+ Assert(qc->commandTag != COMMANDTAG_UNKNOWN);
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index bb85b5e52a..b7b7c952a7 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -83,10 +83,9 @@ static void ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletion *qc);
static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
-
/*
* CommandIsReadOnly: is an executable query read-only?
*
@@ -467,7 +466,6 @@ CheckRestrictedOperation(const char *cmdname)
cmdname)));
}
-
/*
* ProcessUtility
* general utility function invoker
@@ -480,16 +478,13 @@ CheckRestrictedOperation(const char *cmdname)
* queryEnv: environment for parse through execution (e.g., ephemeral named
* tables like trigger transition tables). May be NULL.
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store command completion status data.
*
* Caller MUST supply a queryString; it is not allowed (anymore) to pass NULL.
* If you really don't have source text, you can pass a constant string,
* perhaps "(query not available)".
*
- * completionTag is only set nonempty if we want to return a nondefault status.
- *
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*
* Note for users of ProcessUtility_hook: the same queryString may be passed
* to multiple invocations of ProcessUtility when processing a query string
@@ -507,7 +502,7 @@ ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Assert(IsA(pstmt, PlannedStmt));
Assert(pstmt->commandType == CMD_UTILITY);
@@ -521,11 +516,11 @@ ProcessUtility(PlannedStmt *pstmt,
if (ProcessUtility_hook)
(*ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
/*
@@ -546,7 +541,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -562,18 +557,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (readonly_flags != COMMAND_IS_STRICTLY_READ_ONLY &&
(XactReadOnly || IsInParallelMode()))
{
- const char *commandtag = CreateCommandTag(parsetree);
+ CommandTag commandtag = CreateCommandTag(parsetree);
if ((readonly_flags & COMMAND_OK_IN_READ_ONLY_TXN) == 0)
- PreventCommandIfReadOnly(commandtag);
+ PreventCommandTagIfReadOnly(commandtag);
if ((readonly_flags & COMMAND_OK_IN_PARALLEL_MODE) == 0)
- PreventCommandIfParallelMode(commandtag);
+ PreventCommandTagIfParallelMode(commandtag);
if ((readonly_flags & COMMAND_OK_IN_RECOVERY) == 0)
- PreventCommandDuringRecovery(commandtag);
+ PreventCommandTagDuringRecovery(commandtag);
}
- if (completionTag)
- completionTag[0] = '\0';
+ if (qc)
+ InitializeQueryCompletion(qc);
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -623,18 +618,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case TRANS_STMT_COMMIT:
if (!EndTransactionBlock(stmt->chain))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQueryCompletion(qc, COMMANDTAG_ROLLBACK, 0);
}
break;
case TRANS_STMT_PREPARE:
if (!PrepareTransactionBlock(stmt->gid))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQueryCompletion(qc, COMMANDTAG_ROLLBACK, 0);
}
break;
@@ -693,8 +688,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case T_FetchStmt:
- PerformPortalFetch((FetchStmt *) parsetree, dest,
- completionTag);
+ PerformPortalFetch((FetchStmt *) parsetree, dest, qc);
break;
case T_DoStmt:
@@ -729,9 +723,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
DoCopy(pstate, (CopyStmt *) parsetree,
pstmt->stmt_location, pstmt->stmt_len,
&processed);
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "COPY " UINT64_FORMAT, processed);
+ if (qc)
+ SetQueryCompletion(qc, COMMANDTAG_COPY, processed);
}
break;
@@ -745,7 +738,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ExecuteQuery(pstate,
(ExecuteStmt *) parsetree, NULL,
params,
- dest, completionTag);
+ dest, qc);
break;
case T_DeallocateStmt:
@@ -974,7 +967,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecuteGrantStmt(stmt);
}
@@ -987,7 +980,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecDropStmt(stmt, isTopLevel);
}
@@ -1000,7 +993,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecRenameStmt(stmt);
}
@@ -1013,7 +1006,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectDependsStmt(stmt, NULL);
}
@@ -1026,7 +1019,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
}
@@ -1039,7 +1032,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterOwnerStmt(stmt);
}
@@ -1052,7 +1045,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
CommentObject(stmt);
break;
@@ -1065,7 +1058,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecSecLabelStmt(stmt);
break;
@@ -1075,7 +1068,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
break;
}
@@ -1102,7 +1095,7 @@ ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -1605,7 +1598,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateTableAsStmt:
address = ExecCreateTableAs(pstate, (CreateTableAsStmt *) parsetree,
- params, queryEnv, completionTag);
+ params, queryEnv, qc);
break;
case T_RefreshMatViewStmt:
@@ -1620,7 +1613,7 @@ ProcessUtilitySlow(ParseState *pstate,
PG_TRY();
{
address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
- queryString, params, completionTag);
+ queryString, params, qc);
}
PG_FINALLY();
{
@@ -2099,137 +2092,137 @@ UtilityContainsQuery(Node *parsetree)
*
* This covers most cases where ALTER is used with an ObjectType enum.
*/
-static const char *
+static CommandTag
AlterObjectTypeCommandTag(ObjectType objtype)
{
- const char *tag;
+ CommandTag tag;
switch (objtype)
{
case OBJECT_AGGREGATE:
- tag = "ALTER AGGREGATE";
+ tag = COMMANDTAG_ALTER_AGGREGATE;
break;
case OBJECT_ATTRIBUTE:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case OBJECT_CAST:
- tag = "ALTER CAST";
+ tag = COMMANDTAG_ALTER_CAST;
break;
case OBJECT_COLLATION:
- tag = "ALTER COLLATION";
+ tag = COMMANDTAG_ALTER_COLLATION;
break;
case OBJECT_COLUMN:
- tag = "ALTER TABLE";
+ tag = COMMANDTAG_ALTER_TABLE;
break;
case OBJECT_CONVERSION:
- tag = "ALTER CONVERSION";
+ tag = COMMANDTAG_ALTER_CONVERSION;
break;
case OBJECT_DATABASE:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case OBJECT_DOMAIN:
case OBJECT_DOMCONSTRAINT:
- tag = "ALTER DOMAIN";
+ tag = COMMANDTAG_ALTER_DOMAIN;
break;
case OBJECT_EXTENSION:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case OBJECT_FDW:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "ALTER SERVER";
+ tag = COMMANDTAG_ALTER_SERVER;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "ALTER FOREIGN TABLE";
+ tag = COMMANDTAG_ALTER_FOREIGN_TABLE;
break;
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = COMMANDTAG_ALTER_FUNCTION;
break;
case OBJECT_INDEX:
- tag = "ALTER INDEX";
+ tag = COMMANDTAG_ALTER_INDEX;
break;
case OBJECT_LANGUAGE:
- tag = "ALTER LANGUAGE";
+ tag = COMMANDTAG_ALTER_LANGUAGE;
break;
case OBJECT_LARGEOBJECT:
- tag = "ALTER LARGE OBJECT";
+ tag = COMMANDTAG_ALTER_LARGE_OBJECT;
break;
case OBJECT_OPCLASS:
- tag = "ALTER OPERATOR CLASS";
+ tag = COMMANDTAG_ALTER_OPERATOR_CLASS;
break;
case OBJECT_OPERATOR:
- tag = "ALTER OPERATOR";
+ tag = COMMANDTAG_ALTER_OPERATOR;
break;
case OBJECT_OPFAMILY:
- tag = "ALTER OPERATOR FAMILY";
+ tag = COMMANDTAG_ALTER_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "ALTER POLICY";
+ tag = COMMANDTAG_ALTER_POLICY;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = COMMANDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROLE:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = COMMANDTAG_ALTER_ROUTINE;
break;
case OBJECT_RULE:
- tag = "ALTER RULE";
+ tag = COMMANDTAG_ALTER_RULE;
break;
case OBJECT_SCHEMA:
- tag = "ALTER SCHEMA";
+ tag = COMMANDTAG_ALTER_SCHEMA;
break;
case OBJECT_SEQUENCE:
- tag = "ALTER SEQUENCE";
+ tag = COMMANDTAG_ALTER_SEQUENCE;
break;
case OBJECT_TABLE:
case OBJECT_TABCONSTRAINT:
- tag = "ALTER TABLE";
+ tag = COMMANDTAG_ALTER_TABLE;
break;
case OBJECT_TABLESPACE:
- tag = "ALTER TABLESPACE";
+ tag = COMMANDTAG_ALTER_TABLESPACE;
break;
case OBJECT_TRIGGER:
- tag = "ALTER TRIGGER";
+ tag = COMMANDTAG_ALTER_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "ALTER EVENT TRIGGER";
+ tag = COMMANDTAG_ALTER_EVENT_TRIGGER;
break;
case OBJECT_TSCONFIGURATION:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_TSDICTIONARY:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSPARSER:
- tag = "ALTER TEXT SEARCH PARSER";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSTEMPLATE:
- tag = "ALTER TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TYPE:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case OBJECT_VIEW:
- tag = "ALTER VIEW";
+ tag = COMMANDTAG_ALTER_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "ALTER MATERIALIZED VIEW";
+ tag = COMMANDTAG_ALTER_MATERIALIZED_VIEW;
break;
case OBJECT_PUBLICATION:
- tag = "ALTER PUBLICATION";
+ tag = COMMANDTAG_ALTER_PUBLICATION;
break;
case OBJECT_SUBSCRIPTION:
- tag = "ALTER SUBSCRIPTION";
+ tag = COMMANDTAG_ALTER_SUBSCRIPTION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "ALTER STATISTICS";
+ tag = COMMANDTAG_ALTER_STATISTICS;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
@@ -2238,20 +2231,17 @@ AlterObjectTypeCommandTag(ObjectType objtype)
/*
* CreateCommandTag
- * utility to get a string representation of the command operation,
+ * utility to get a CommandTag for the command operation,
* given either a raw (un-analyzed) parsetree, an analyzed Query,
* or a PlannedStmt.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
- *
- * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
- * Also, the result must point at a true constant (permanent storage).
*/
-const char *
+CommandTag
CreateCommandTag(Node *parsetree)
{
- const char *tag;
+ CommandTag tag;
switch (nodeTag(parsetree))
{
@@ -2262,19 +2252,19 @@ CreateCommandTag(Node *parsetree)
/* raw plannable queries */
case T_InsertStmt:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case T_DeleteStmt:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case T_UpdateStmt:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case T_SelectStmt:
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
/* utility statements --- same whether raw or cooked */
@@ -2285,51 +2275,51 @@ CreateCommandTag(Node *parsetree)
switch (stmt->kind)
{
case TRANS_STMT_BEGIN:
- tag = "BEGIN";
+ tag = COMMANDTAG_BEGIN;
break;
case TRANS_STMT_START:
- tag = "START TRANSACTION";
+ tag = COMMANDTAG_START_TRANSACTION;
break;
case TRANS_STMT_COMMIT:
- tag = "COMMIT";
+ tag = COMMANDTAG_COMMIT;
break;
case TRANS_STMT_ROLLBACK:
case TRANS_STMT_ROLLBACK_TO:
- tag = "ROLLBACK";
+ tag = COMMANDTAG_ROLLBACK;
break;
case TRANS_STMT_SAVEPOINT:
- tag = "SAVEPOINT";
+ tag = COMMANDTAG_SAVEPOINT;
break;
case TRANS_STMT_RELEASE:
- tag = "RELEASE";
+ tag = COMMANDTAG_RELEASE;
break;
case TRANS_STMT_PREPARE:
- tag = "PREPARE TRANSACTION";
+ tag = COMMANDTAG_PREPARE_TRANSACTION;
break;
case TRANS_STMT_COMMIT_PREPARED:
- tag = "COMMIT PREPARED";
+ tag = COMMANDTAG_COMMIT_PREPARED;
break;
case TRANS_STMT_ROLLBACK_PREPARED:
- tag = "ROLLBACK PREPARED";
+ tag = COMMANDTAG_ROLLBACK_PREPARED;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
break;
case T_DeclareCursorStmt:
- tag = "DECLARE CURSOR";
+ tag = COMMANDTAG_DECLARE_CURSOR;
break;
case T_ClosePortalStmt:
@@ -2337,9 +2327,9 @@ CreateCommandTag(Node *parsetree)
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
if (stmt->portalname == NULL)
- tag = "CLOSE CURSOR ALL";
+ tag = COMMANDTAG_CLOSE_CURSOR_ALL;
else
- tag = "CLOSE CURSOR";
+ tag = COMMANDTAG_CLOSE_CURSOR;
}
break;
@@ -2347,209 +2337,209 @@ CreateCommandTag(Node *parsetree)
{
FetchStmt *stmt = (FetchStmt *) parsetree;
- tag = (stmt->ismove) ? "MOVE" : "FETCH";
+ tag = (stmt->ismove) ? COMMANDTAG_MOVE : COMMANDTAG_FETCH;
}
break;
case T_CreateDomainStmt:
- tag = "CREATE DOMAIN";
+ tag = COMMANDTAG_CREATE_DOMAIN;
break;
case T_CreateSchemaStmt:
- tag = "CREATE SCHEMA";
+ tag = COMMANDTAG_CREATE_SCHEMA;
break;
case T_CreateStmt:
- tag = "CREATE TABLE";
+ tag = COMMANDTAG_CREATE_TABLE;
break;
case T_CreateTableSpaceStmt:
- tag = "CREATE TABLESPACE";
+ tag = COMMANDTAG_CREATE_TABLESPACE;
break;
case T_DropTableSpaceStmt:
- tag = "DROP TABLESPACE";
+ tag = COMMANDTAG_DROP_TABLESPACE;
break;
case T_AlterTableSpaceOptionsStmt:
- tag = "ALTER TABLESPACE";
+ tag = COMMANDTAG_ALTER_TABLESPACE;
break;
case T_CreateExtensionStmt:
- tag = "CREATE EXTENSION";
+ tag = COMMANDTAG_CREATE_EXTENSION;
break;
case T_AlterExtensionStmt:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case T_AlterExtensionContentsStmt:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case T_CreateFdwStmt:
- tag = "CREATE FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER;
break;
case T_AlterFdwStmt:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case T_CreateForeignServerStmt:
- tag = "CREATE SERVER";
+ tag = COMMANDTAG_CREATE_SERVER;
break;
case T_AlterForeignServerStmt:
- tag = "ALTER SERVER";
+ tag = COMMANDTAG_ALTER_SERVER;
break;
case T_CreateUserMappingStmt:
- tag = "CREATE USER MAPPING";
+ tag = COMMANDTAG_CREATE_USER_MAPPING;
break;
case T_AlterUserMappingStmt:
- tag = "ALTER USER MAPPING";
+ tag = COMMANDTAG_ALTER_USER_MAPPING;
break;
case T_DropUserMappingStmt:
- tag = "DROP USER MAPPING";
+ tag = COMMANDTAG_DROP_USER_MAPPING;
break;
case T_CreateForeignTableStmt:
- tag = "CREATE FOREIGN TABLE";
+ tag = COMMANDTAG_CREATE_FOREIGN_TABLE;
break;
case T_ImportForeignSchemaStmt:
- tag = "IMPORT FOREIGN SCHEMA";
+ tag = COMMANDTAG_IMPORT_FOREIGN_SCHEMA;
break;
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_TABLE:
- tag = "DROP TABLE";
+ tag = COMMANDTAG_DROP_TABLE;
break;
case OBJECT_SEQUENCE:
- tag = "DROP SEQUENCE";
+ tag = COMMANDTAG_DROP_SEQUENCE;
break;
case OBJECT_VIEW:
- tag = "DROP VIEW";
+ tag = COMMANDTAG_DROP_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "DROP MATERIALIZED VIEW";
+ tag = COMMANDTAG_DROP_MATERIALIZED_VIEW;
break;
case OBJECT_INDEX:
- tag = "DROP INDEX";
+ tag = COMMANDTAG_DROP_INDEX;
break;
case OBJECT_TYPE:
- tag = "DROP TYPE";
+ tag = COMMANDTAG_DROP_TYPE;
break;
case OBJECT_DOMAIN:
- tag = "DROP DOMAIN";
+ tag = COMMANDTAG_DROP_DOMAIN;
break;
case OBJECT_COLLATION:
- tag = "DROP COLLATION";
+ tag = COMMANDTAG_DROP_COLLATION;
break;
case OBJECT_CONVERSION:
- tag = "DROP CONVERSION";
+ tag = COMMANDTAG_DROP_CONVERSION;
break;
case OBJECT_SCHEMA:
- tag = "DROP SCHEMA";
+ tag = COMMANDTAG_DROP_SCHEMA;
break;
case OBJECT_TSPARSER:
- tag = "DROP TEXT SEARCH PARSER";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "DROP TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "DROP TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "DROP TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "DROP FOREIGN TABLE";
+ tag = COMMANDTAG_DROP_FOREIGN_TABLE;
break;
case OBJECT_EXTENSION:
- tag = "DROP EXTENSION";
+ tag = COMMANDTAG_DROP_EXTENSION;
break;
case OBJECT_FUNCTION:
- tag = "DROP FUNCTION";
+ tag = COMMANDTAG_DROP_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "DROP PROCEDURE";
+ tag = COMMANDTAG_DROP_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "DROP ROUTINE";
+ tag = COMMANDTAG_DROP_ROUTINE;
break;
case OBJECT_AGGREGATE:
- tag = "DROP AGGREGATE";
+ tag = COMMANDTAG_DROP_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "DROP OPERATOR";
+ tag = COMMANDTAG_DROP_OPERATOR;
break;
case OBJECT_LANGUAGE:
- tag = "DROP LANGUAGE";
+ tag = COMMANDTAG_DROP_LANGUAGE;
break;
case OBJECT_CAST:
- tag = "DROP CAST";
+ tag = COMMANDTAG_DROP_CAST;
break;
case OBJECT_TRIGGER:
- tag = "DROP TRIGGER";
+ tag = COMMANDTAG_DROP_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "DROP EVENT TRIGGER";
+ tag = COMMANDTAG_DROP_EVENT_TRIGGER;
break;
case OBJECT_RULE:
- tag = "DROP RULE";
+ tag = COMMANDTAG_DROP_RULE;
break;
case OBJECT_FDW:
- tag = "DROP FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "DROP SERVER";
+ tag = COMMANDTAG_DROP_SERVER;
break;
case OBJECT_OPCLASS:
- tag = "DROP OPERATOR CLASS";
+ tag = COMMANDTAG_DROP_OPERATOR_CLASS;
break;
case OBJECT_OPFAMILY:
- tag = "DROP OPERATOR FAMILY";
+ tag = COMMANDTAG_DROP_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "DROP POLICY";
+ tag = COMMANDTAG_DROP_POLICY;
break;
case OBJECT_TRANSFORM:
- tag = "DROP TRANSFORM";
+ tag = COMMANDTAG_DROP_TRANSFORM;
break;
case OBJECT_ACCESS_METHOD:
- tag = "DROP ACCESS METHOD";
+ tag = COMMANDTAG_DROP_ACCESS_METHOD;
break;
case OBJECT_PUBLICATION:
- tag = "DROP PUBLICATION";
+ tag = COMMANDTAG_DROP_PUBLICATION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "DROP STATISTICS";
+ tag = COMMANDTAG_DROP_STATISTICS;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_TruncateStmt:
- tag = "TRUNCATE TABLE";
+ tag = COMMANDTAG_TRUNCATE_TABLE;
break;
case T_CommentStmt:
- tag = "COMMENT";
+ tag = COMMANDTAG_COMMENT;
break;
case T_SecLabelStmt:
- tag = "SECURITY LABEL";
+ tag = COMMANDTAG_SECURITY_LABEL;
break;
case T_CopyStmt:
- tag = "COPY";
+ tag = COMMANDTAG_COPY;
break;
case T_RenameStmt:
@@ -2584,23 +2574,23 @@ CreateCommandTag(Node *parsetree)
break;
case T_AlterDomainStmt:
- tag = "ALTER DOMAIN";
+ tag = COMMANDTAG_ALTER_DOMAIN;
break;
case T_AlterFunctionStmt:
switch (((AlterFunctionStmt *) parsetree)->objtype)
{
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = COMMANDTAG_ALTER_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = COMMANDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = COMMANDTAG_ALTER_ROUTINE;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
@@ -2608,7 +2598,7 @@ CreateCommandTag(Node *parsetree)
{
GrantStmt *stmt = (GrantStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
+ tag = (stmt->is_grant) ? COMMANDTAG_GRANT : COMMANDTAG_REVOKE;
}
break;
@@ -2616,145 +2606,145 @@ CreateCommandTag(Node *parsetree)
{
GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
+ tag = (stmt->is_grant) ? COMMANDTAG_GRANT_ROLE : COMMANDTAG_REVOKE_ROLE;
}
break;
case T_AlterDefaultPrivilegesStmt:
- tag = "ALTER DEFAULT PRIVILEGES";
+ tag = COMMANDTAG_ALTER_DEFAULT_PRIVILEGES;
break;
case T_DefineStmt:
switch (((DefineStmt *) parsetree)->kind)
{
case OBJECT_AGGREGATE:
- tag = "CREATE AGGREGATE";
+ tag = COMMANDTAG_CREATE_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "CREATE OPERATOR";
+ tag = COMMANDTAG_CREATE_OPERATOR;
break;
case OBJECT_TYPE:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case OBJECT_TSPARSER:
- tag = "CREATE TEXT SEARCH PARSER";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "CREATE TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "CREATE TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "CREATE TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_COLLATION:
- tag = "CREATE COLLATION";
+ tag = COMMANDTAG_CREATE_COLLATION;
break;
case OBJECT_ACCESS_METHOD:
- tag = "CREATE ACCESS METHOD";
+ tag = COMMANDTAG_CREATE_ACCESS_METHOD;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_CompositeTypeStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_CreateEnumStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_CreateRangeStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_AlterEnumStmt:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case T_ViewStmt:
- tag = "CREATE VIEW";
+ tag = COMMANDTAG_CREATE_VIEW;
break;
case T_CreateFunctionStmt:
if (((CreateFunctionStmt *) parsetree)->is_procedure)
- tag = "CREATE PROCEDURE";
+ tag = COMMANDTAG_CREATE_PROCEDURE;
else
- tag = "CREATE FUNCTION";
+ tag = COMMANDTAG_CREATE_FUNCTION;
break;
case T_IndexStmt:
- tag = "CREATE INDEX";
+ tag = COMMANDTAG_CREATE_INDEX;
break;
case T_RuleStmt:
- tag = "CREATE RULE";
+ tag = COMMANDTAG_CREATE_RULE;
break;
case T_CreateSeqStmt:
- tag = "CREATE SEQUENCE";
+ tag = COMMANDTAG_CREATE_SEQUENCE;
break;
case T_AlterSeqStmt:
- tag = "ALTER SEQUENCE";
+ tag = COMMANDTAG_ALTER_SEQUENCE;
break;
case T_DoStmt:
- tag = "DO";
+ tag = COMMANDTAG_DO;
break;
case T_CreatedbStmt:
- tag = "CREATE DATABASE";
+ tag = COMMANDTAG_CREATE_DATABASE;
break;
case T_AlterDatabaseStmt:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case T_AlterDatabaseSetStmt:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case T_DropdbStmt:
- tag = "DROP DATABASE";
+ tag = COMMANDTAG_DROP_DATABASE;
break;
case T_NotifyStmt:
- tag = "NOTIFY";
+ tag = COMMANDTAG_NOTIFY;
break;
case T_ListenStmt:
- tag = "LISTEN";
+ tag = COMMANDTAG_LISTEN;
break;
case T_UnlistenStmt:
- tag = "UNLISTEN";
+ tag = COMMANDTAG_UNLISTEN;
break;
case T_LoadStmt:
- tag = "LOAD";
+ tag = COMMANDTAG_LOAD;
break;
case T_CallStmt:
- tag = "CALL";
+ tag = COMMANDTAG_CALL;
break;
case T_ClusterStmt:
- tag = "CLUSTER";
+ tag = COMMANDTAG_CLUSTER;
break;
case T_VacuumStmt:
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
- tag = "VACUUM";
+ tag = COMMANDTAG_VACUUM;
else
- tag = "ANALYZE";
+ tag = COMMANDTAG_ANALYZE;
break;
case T_ExplainStmt:
- tag = "EXPLAIN";
+ tag = COMMANDTAG_EXPLAIN;
break;
case T_CreateTableAsStmt:
@@ -2762,24 +2752,24 @@ CreateCommandTag(Node *parsetree)
{
case OBJECT_TABLE:
if (((CreateTableAsStmt *) parsetree)->is_select_into)
- tag = "SELECT INTO";
+ tag = COMMANDTAG_SELECT_INTO;
else
- tag = "CREATE TABLE AS";
+ tag = COMMANDTAG_CREATE_TABLE_AS;
break;
case OBJECT_MATVIEW:
- tag = "CREATE MATERIALIZED VIEW";
+ tag = COMMANDTAG_CREATE_MATERIALIZED_VIEW;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_RefreshMatViewStmt:
- tag = "REFRESH MATERIALIZED VIEW";
+ tag = COMMANDTAG_REFRESH_MATERIALIZED_VIEW;
break;
case T_AlterSystemStmt:
- tag = "ALTER SYSTEM";
+ tag = COMMANDTAG_ALTER_SYSTEM;
break;
case T_VariableSetStmt:
@@ -2789,183 +2779,183 @@ CreateCommandTag(Node *parsetree)
case VAR_SET_CURRENT:
case VAR_SET_DEFAULT:
case VAR_SET_MULTI:
- tag = "SET";
+ tag = COMMANDTAG_SET;
break;
case VAR_RESET:
case VAR_RESET_ALL:
- tag = "RESET";
+ tag = COMMANDTAG_RESET;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_VariableShowStmt:
- tag = "SHOW";
+ tag = COMMANDTAG_SHOW;
break;
case T_DiscardStmt:
switch (((DiscardStmt *) parsetree)->target)
{
case DISCARD_ALL:
- tag = "DISCARD ALL";
+ tag = COMMANDTAG_DISCARD_ALL;
break;
case DISCARD_PLANS:
- tag = "DISCARD PLANS";
+ tag = COMMANDTAG_DISCARD_PLANS;
break;
case DISCARD_TEMP:
- tag = "DISCARD TEMP";
+ tag = COMMANDTAG_DISCARD_TEMP;
break;
case DISCARD_SEQUENCES:
- tag = "DISCARD SEQUENCES";
+ tag = COMMANDTAG_DISCARD_SEQUENCES;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_CreateTransformStmt:
- tag = "CREATE TRANSFORM";
+ tag = COMMANDTAG_CREATE_TRANSFORM;
break;
case T_CreateTrigStmt:
- tag = "CREATE TRIGGER";
+ tag = COMMANDTAG_CREATE_TRIGGER;
break;
case T_CreateEventTrigStmt:
- tag = "CREATE EVENT TRIGGER";
+ tag = COMMANDTAG_CREATE_EVENT_TRIGGER;
break;
case T_AlterEventTrigStmt:
- tag = "ALTER EVENT TRIGGER";
+ tag = COMMANDTAG_ALTER_EVENT_TRIGGER;
break;
case T_CreatePLangStmt:
- tag = "CREATE LANGUAGE";
+ tag = COMMANDTAG_CREATE_LANGUAGE;
break;
case T_CreateRoleStmt:
- tag = "CREATE ROLE";
+ tag = COMMANDTAG_CREATE_ROLE;
break;
case T_AlterRoleStmt:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case T_AlterRoleSetStmt:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case T_DropRoleStmt:
- tag = "DROP ROLE";
+ tag = COMMANDTAG_DROP_ROLE;
break;
case T_DropOwnedStmt:
- tag = "DROP OWNED";
+ tag = COMMANDTAG_DROP_OWNED;
break;
case T_ReassignOwnedStmt:
- tag = "REASSIGN OWNED";
+ tag = COMMANDTAG_REASSIGN_OWNED;
break;
case T_LockStmt:
- tag = "LOCK TABLE";
+ tag = COMMANDTAG_LOCK_TABLE;
break;
case T_ConstraintsSetStmt:
- tag = "SET CONSTRAINTS";
+ tag = COMMANDTAG_SET_CONSTRAINTS;
break;
case T_CheckPointStmt:
- tag = "CHECKPOINT";
+ tag = COMMANDTAG_CHECKPOINT;
break;
case T_ReindexStmt:
- tag = "REINDEX";
+ tag = COMMANDTAG_REINDEX;
break;
case T_CreateConversionStmt:
- tag = "CREATE CONVERSION";
+ tag = COMMANDTAG_CREATE_CONVERSION;
break;
case T_CreateCastStmt:
- tag = "CREATE CAST";
+ tag = COMMANDTAG_CREATE_CAST;
break;
case T_CreateOpClassStmt:
- tag = "CREATE OPERATOR CLASS";
+ tag = COMMANDTAG_CREATE_OPERATOR_CLASS;
break;
case T_CreateOpFamilyStmt:
- tag = "CREATE OPERATOR FAMILY";
+ tag = COMMANDTAG_CREATE_OPERATOR_FAMILY;
break;
case T_AlterOpFamilyStmt:
- tag = "ALTER OPERATOR FAMILY";
+ tag = COMMANDTAG_ALTER_OPERATOR_FAMILY;
break;
case T_AlterOperatorStmt:
- tag = "ALTER OPERATOR";
+ tag = COMMANDTAG_ALTER_OPERATOR;
break;
case T_AlterTSDictionaryStmt:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case T_AlterTSConfigurationStmt:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case T_CreatePolicyStmt:
- tag = "CREATE POLICY";
+ tag = COMMANDTAG_CREATE_POLICY;
break;
case T_AlterPolicyStmt:
- tag = "ALTER POLICY";
+ tag = COMMANDTAG_ALTER_POLICY;
break;
case T_CreateAmStmt:
- tag = "CREATE ACCESS METHOD";
+ tag = COMMANDTAG_CREATE_ACCESS_METHOD;
break;
case T_CreatePublicationStmt:
- tag = "CREATE PUBLICATION";
+ tag = COMMANDTAG_CREATE_PUBLICATION;
break;
case T_AlterPublicationStmt:
- tag = "ALTER PUBLICATION";
+ tag = COMMANDTAG_ALTER_PUBLICATION;
break;
case T_CreateSubscriptionStmt:
- tag = "CREATE SUBSCRIPTION";
+ tag = COMMANDTAG_CREATE_SUBSCRIPTION;
break;
case T_AlterSubscriptionStmt:
- tag = "ALTER SUBSCRIPTION";
+ tag = COMMANDTAG_ALTER_SUBSCRIPTION;
break;
case T_DropSubscriptionStmt:
- tag = "DROP SUBSCRIPTION";
+ tag = COMMANDTAG_DROP_SUBSCRIPTION;
break;
case T_AlterCollationStmt:
- tag = "ALTER COLLATION";
+ tag = COMMANDTAG_ALTER_COLLATION;
break;
case T_PrepareStmt:
- tag = "PREPARE";
+ tag = COMMANDTAG_PREPARE;
break;
case T_ExecuteStmt:
- tag = "EXECUTE";
+ tag = COMMANDTAG_EXECUTE;
break;
case T_CreateStatsStmt:
- tag = "CREATE STATISTICS";
+ tag = COMMANDTAG_CREATE_STATISTICS;
break;
case T_AlterStatsStmt:
- tag = "ALTER STATISTICS";
+ tag = COMMANDTAG_ALTER_STATISTICS;
break;
case T_DeallocateStmt:
@@ -2973,9 +2963,9 @@ CreateCommandTag(Node *parsetree)
DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
if (stmt->name == NULL)
- tag = "DEALLOCATE ALL";
+ tag = COMMANDTAG_DEALLOCATE_ALL;
else
- tag = "DEALLOCATE";
+ tag = COMMANDTAG_DEALLOCATE;
}
break;
@@ -2999,33 +2989,33 @@ CreateCommandTag(Node *parsetree)
switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = COMMANDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = COMMANDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
}
}
else
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3033,7 +3023,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
@@ -3059,33 +3049,33 @@ CreateCommandTag(Node *parsetree)
switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = COMMANDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = COMMANDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
else
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3093,7 +3083,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
@@ -3102,7 +3092,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
diff --git a/src/backend/utils/.gitignore b/src/backend/utils/.gitignore
index 0685556959..7d6dfa45a5 100644
--- a/src/backend/utils/.gitignore
+++ b/src/backend/utils/.gitignore
@@ -1,6 +1,9 @@
+/commandtag_behavior.h
+/commandtag_enum.h
/fmgrtab.c
/fmgroids.h
/fmgrprotos.h
/fmgr-stamp
+/commandtag-stamp
/probes.h
/errcodes.h
diff --git a/src/backend/utils/Makefile b/src/backend/utils/Makefile
index b91028ddfd..4e27b2d80c 100644
--- a/src/backend/utils/Makefile
+++ b/src/backend/utils/Makefile
@@ -13,23 +13,29 @@ subdir = src/backend/utils
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-OBJS = fmgrtab.o
+OBJS = commandtag.o fmgrtab.o
SUBDIRS = adt cache error fmgr hash init mb misc mmgr resowner sort time
# location of Catalog.pm
catalogdir = $(top_srcdir)/src/backend/catalog
+# location of commandtag.dat
+headerdir = $(top_srcdir)/src/include/utils
+
+# location to write generated headers
+sourcedir = $(top_srcdir)/src/backend/utils
+
include $(top_srcdir)/src/backend/common.mk
all: distprep probes.h generated-header-symlinks
-distprep: fmgr-stamp errcodes.h
+distprep: commandtag-stamp fmgr-stamp errcodes.h
.PHONY: generated-header-symlinks
generated-header-symlinks: $(top_builddir)/src/include/utils/header-stamp $(top_builddir)/src/include/utils/probes.h
-$(SUBDIRS:%=%-recursive): fmgr-stamp errcodes.h
+$(SUBDIRS:%=%-recursive): commandtag-stamp fmgr-stamp errcodes.h
# fmgr-stamp records the last time we ran Gen_fmgrtab.pl. We don't rely on
# the timestamps of the individual output files, because the Perl script
@@ -38,6 +44,10 @@ fmgr-stamp: Gen_fmgrtab.pl $(catalogdir)/Catalog.pm $(top_srcdir)/src/include/ca
$(PERL) -I $(catalogdir) $< --include-path=$(top_srcdir)/src/include/ $(top_srcdir)/src/include/catalog/pg_proc.dat
touch $@
+commandtag-stamp: gencommandtag.pl $(headerdir)/commandtag.dat
+ $(PERL) -I $(top_srcdir)/src/include/utils $< --headerdir=$(headerdir) --sourcedir=$(sourcedir) --inputfile=$(headerdir)/commandtag.dat
+ touch $@
+
errcodes.h: $(top_srcdir)/src/backend/utils/errcodes.txt generate-errcodes.pl
$(PERL) $(srcdir)/generate-errcodes.pl $< > $@
@@ -62,10 +72,10 @@ endif
# These generated headers must be symlinked into builddir/src/include/,
# using absolute links for the reasons explained in src/backend/Makefile.
# We use header-stamp to record that we've done this because the symlinks
-# themselves may appear older than fmgr-stamp.
-$(top_builddir)/src/include/utils/header-stamp: fmgr-stamp errcodes.h
+# themselves may appear older than commandtag-stamp and fmgr-stamp.
+$(top_builddir)/src/include/utils/header-stamp: commandtag-stamp fmgr-stamp errcodes.h
prereqdir=`cd '$(dir $<)' >/dev/null && pwd` && \
- cd '$(dir $@)' && for file in fmgroids.h fmgrprotos.h errcodes.h; do \
+ cd '$(dir $@)' && for file in commandtag_enum.h commandtag_behavior.h fmgroids.h fmgrprotos.h errcodes.h; do \
rm -f $$file && $(LN_S) "$$prereqdir/$$file" . ; \
done
touch $@
@@ -87,10 +97,10 @@ installdirs:
uninstall-data:
rm -f $(addprefix '$(DESTDIR)$(datadir)'/, errcodes.txt)
-# fmgroids.h, fmgrprotos.h, fmgrtab.c, fmgr-stamp, and errcodes.h are in the
+# fmgroids.h, fmgrprotos.h, fmgrtab.c, commandtag-stamp, fmgr-stamp, and errcodes.h are in the
# distribution tarball, so they are not cleaned here.
clean:
rm -f probes.h
maintainer-clean: clean
- rm -f fmgroids.h fmgrprotos.h fmgrtab.c fmgr-stamp errcodes.h
+ rm -f commandtag_enum.h commandtag_behavior.h commandtag-stamp fmgroids.h fmgrprotos.h fmgrtab.c fmgr-stamp errcodes.h
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 1b63048a77..a960d7ca80 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -51,7 +51,7 @@ static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD;
static void BuildEventTriggerCache(void);
static void InvalidateEventCacheCallback(Datum arg,
int cacheid, uint32 hashvalue);
-static int DecodeTextArrayToCString(Datum array, char ***cstringp);
+static void DecodeTextArrayToBitmapset(Datum array, Bitmapset **bms);
/*
* Search the event cache by trigger event.
@@ -180,10 +180,7 @@ BuildEventTriggerCache(void)
evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags,
RelationGetDescr(rel), &evttags_isnull);
if (!evttags_isnull)
- {
- item->ntags = DecodeTextArrayToCString(evttags, &item->tag);
- qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp);
- }
+ DecodeTextArrayToBitmapset(evttags, &item->tagset);
/* Add to cache entry. */
entry = hash_search(cache, &event, HASH_ENTER, &found);
@@ -215,18 +212,18 @@ BuildEventTriggerCache(void)
}
/*
- * Decode text[] to an array of C strings.
+ * Decode text[] to a Bitmapset of CommandTags.
*
* We could avoid a bit of overhead here if we were willing to duplicate some
* of the logic from deconstruct_array, but it doesn't seem worth the code
* complexity.
*/
-static int
-DecodeTextArrayToCString(Datum array, char ***cstringp)
+static void
+DecodeTextArrayToBitmapset(Datum array, Bitmapset **tagset)
{
ArrayType *arr = DatumGetArrayTypeP(array);
Datum *elems;
- char **cstring;
+ Bitmapset *bms;
int i;
int nelems;
@@ -234,13 +231,15 @@ DecodeTextArrayToCString(Datum array, char ***cstringp)
elog(ERROR, "expected 1-D text array");
deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems);
- cstring = palloc(nelems * sizeof(char *));
- for (i = 0; i < nelems; ++i)
- cstring[i] = TextDatumGetCString(elems[i]);
+ for (bms = NULL, i = 0; i < nelems; ++i)
+ {
+ char *str = TextDatumGetCString(elems[i]);
+ bms = bms_add_member(bms, GetCommandTagEnum(str));
+ pfree(str);
+ }
pfree(elems);
- *cstringp = cstring;
- return nelems;
+ *tagset = bms;
}
/*
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index c47be0ba4c..53401bd4e9 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -163,7 +163,7 @@ InitPlanCache(void)
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
MemoryContext source_context;
@@ -246,7 +246,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
CachedPlanSource *
CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
diff --git a/src/backend/utils/commandtag.c b/src/backend/utils/commandtag.c
new file mode 100644
index 0000000000..c629eca608
--- /dev/null
+++ b/src/backend/utils/commandtag.c
@@ -0,0 +1,119 @@
+/*-------------------------------------------------------------------------
+ *
+ * commandtag.c
+ * Data and routines for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/utils/commandtag.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "c.h"
+#include "miscadmin.h"
+#include "utils/commandtag.h"
+#include "utils/commandtag_enum.h"
+#include "utils/commandtag_behavior.h"
+
+static const unsigned int tag_behavior_length = lengthof(tag_behavior);
+
+static inline bool
+PG_VALID_COMMANDTAG(CommandTag commandTag)
+{
+ return (commandTag >= FIRST_COMMANDTAG &&
+ commandTag <= LAST_COMMANDTAG);
+}
+
+void
+InitializeQueryCompletion(QueryCompletion *qc)
+{
+ qc->commandTag = COMMANDTAG_UNKNOWN;
+ qc->nprocessed = 0;
+}
+
+const char *
+GetCommandTagName(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].name;
+}
+
+bool
+command_tag_display_last_oid(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].display_last_oid;
+}
+
+bool
+command_tag_display_rowcount(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].display_rowcount;
+}
+
+bool
+command_tag_event_trigger_ok(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].event_trigger_ok;
+}
+
+bool
+command_tag_table_rewrite_ok(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].table_rewrite_ok;
+}
+
+/*
+ * Search CommandTag by name
+ *
+ * Returns CommandTag, or COMMANDTAG_UNKNOWN if not recognized
+ */
+CommandTag
+GetCommandTagEnum(const char *commandname)
+{
+ const CommandTagBehavior *base, *last, *position;
+ int result;
+
+ if (commandname == NULL || *commandname == '\0')
+ return COMMANDTAG_UNKNOWN;
+
+ base = tag_behavior;
+ last = tag_behavior + tag_behavior_length - 1;
+ while (last >= base)
+ {
+ position = base + ((last - base) >> 1);
+ result = pg_strcasecmp(commandname, position->name);
+ if (result == 0)
+ return (CommandTag) (position - tag_behavior);
+ else if (result < 0)
+ last = position - 1;
+ else
+ base = position + 1;
+ }
+ return COMMANDTAG_UNKNOWN;
+}
+
+void
+PreventCommandTagIfReadOnly(CommandTag commandTag)
+{
+ PreventCommandIfReadOnly(GetCommandTagName(commandTag));
+}
+
+void
+PreventCommandTagIfParallelMode(CommandTag commandTag)
+{
+ PreventCommandIfParallelMode(GetCommandTagName(commandTag));
+}
+
+void
+PreventCommandTagDuringRecovery(CommandTag commandTag)
+{
+ PreventCommandDuringRecovery(GetCommandTagName(commandTag));
+}
diff --git a/src/backend/utils/gencommandtag.pl b/src/backend/utils/gencommandtag.pl
new file mode 100755
index 0000000000..2f2d37ef1f
--- /dev/null
+++ b/src/backend/utils/gencommandtag.pl
@@ -0,0 +1,285 @@
+#!/usr/bin/perl -w
+#----------------------------------------------------------------------
+#
+# gencommandtag.pl
+# Perl script that generates the commandtag data headers from the
+# specially formatted commandtag.dat data file.
+#
+# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/backend/utils/gencommandtag.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+use File::Basename;
+use File::Spec;
+BEGIN { use lib File::Spec->rel2abs(dirname(__FILE__)); }
+
+use FindBin;
+use lib "$FindBin::RealBin/../catalog/";
+use DataFile;
+
+my $headerdir;
+my $sourcedir;
+my $inputfile;
+my $errorcnt = 0;
+
+GetOptions(
+ 'inputfile=s' => \$inputfile,
+ 'sourcedir=s' => \$sourcedir,
+ 'headerdir=s' => \$headerdir) || usage();
+
+# Sanity check arguments.
+die "input file $inputfile does not exist" unless(-f $inputfile);
+die "header directory $headerdir does not exist" unless(-d $headerdir);
+die "source directory $headerdir does not exist" unless(-d $headerdir);
+
+sub dataerror
+{
+ my ($errmsg, $errhint) = @_;
+ print STDERR "Error encountered while verifying $inputfile:\n";
+ print STDERR $errmsg, "\n";
+ if (defined $errhint)
+ {
+ print STDERR "HINT: ", $errhint, "\n";
+ }
+ print STDERR "Please fix $inputfile\n";
+ exit 1;
+}
+
+# Make sure paths end with a slash.
+$sourcedir .= '/' if (substr($sourcedir, -1) ne '/');
+$headerdir .= '/' if (substr($headerdir, -1) ne '/');
+
+my $enumfile = join('', $sourcedir, "commandtag_enum.h");
+my $arrayfile = join('', $sourcedir, "commandtag_behavior.h");
+
+# Vivify the input data, but be paranoid about eval'ing arbitrary code
+my $data = DataFile::ParseData($inputfile);
+sanity_check_data($data);
+
+# Sort the commandtags based on their stringified representation,
+# not their labels.
+my @sorted = sort { $a->{tagname} cmp $b->{tagname} } @$data;
+
+# Generate all output internally before outputting anything, to avoid
+# partially overwriting generated files under error conditions
+my (@enum_chunks, @behavior_chunks);
+push(@enum_chunks, qq(
+/*-------------------------------------------------------------------------
+ *
+ * commandtag_enum.h
+ * Enumeration of all PostgreSQL command tags.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * NOTES
+ * ******************************
+ * *** DO NOT EDIT THIS FILE! ***
+ * ******************************
+ *
+ * It has been generated by src/backend/utils/gencommandtag.pl
+ *
+ * IDENTIFICATION
+ * src/include/utils/commandtag_enum.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef COMMANDTAG_ENUM_H
+#define COMMANDTAG_ENUM_H
+typedef enum CommandTag
+{
+#define FIRST_COMMANDTAG COMMANDTAG_$sorted[0]->{taglabel})
+);
+
+push(@behavior_chunks, qq(
+/*-------------------------------------------------------------------------
+ *
+ * commandtag_behavior.h
+ * Array of records defining the behavior of each command tag.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * NOTES
+ * ******************************
+ * *** DO NOT EDIT THIS FILE! ***
+ * ******************************
+ *
+ * It has been generated by src/backend/utils/gencommandtag.pl
+ *
+ * IDENTIFICATION
+ * src/include/utils/commandtag_behavior.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef COMMANDTAG_BEHAVIOR_H
+#define COMMANDTAG_BEHAVIOR_H
+const CommandTagBehavior tag_behavior[] = {)
+);
+
+for(my $i = 0; $i < scalar(@sorted); $i++)
+{
+ my $rec = $sorted[$i];
+ my $comma = ($i < $#sorted) ? "," : "";
+ my $enumrow = sprintf("\tCOMMANDTAG_%s%s", $rec->{taglabel}, $comma);
+ push(@enum_chunks, $enumrow);
+
+ my $behaviorrow = sprintf("\t[COMMANDTAG_%s] = {", $rec->{taglabel});
+ $behaviorrow .= sprintf("\n\t\t.name = \"%s\"", $rec->{tagname});
+ $behaviorrow .= ",\n\t\t.event_trigger_ok = true" if ($rec->{event_trigger_ok});
+ $behaviorrow .= ",\n\t\t.table_rewrite_ok = true" if ($rec->{table_rewrite_ok});
+ $behaviorrow .= ",\n\t\t.display_rowcount = true" if ($rec->{display_rowcount});
+ $behaviorrow .= ",\n\t\t.display_last_oid = true" if ($rec->{display_last_oid});
+ $behaviorrow .= "\n\t}$comma";
+ push (@behavior_chunks, $behaviorrow);
+}
+
+push(@enum_chunks,
+qq(#define LAST_COMMANDTAG COMMANDTAG_$sorted[$#sorted]->{taglabel}
+} CommandTag;
+#endif /* COMMANDTAG_ENUM_H */)
+);
+
+push(@behavior_chunks,
+qq(};
+StaticAssertDecl(FIRST_COMMANDTAG == (CommandTag)0, "first command tag");
+StaticAssertDecl(lengthof(tag_behavior) == (LAST_COMMANDTAG + 1),
+ "array length mismatch");
+#endif /* COMMANDTAG_BEHAVIOR_H */)
+);
+
+my $enum_out = IO::File->new(">$enumfile")
+ or die "Cannot write $enumfile: $!";
+$enum_out->print(join("\n", @enum_chunks));
+$enum_out->close();
+
+my $array_out = IO::File->new(">$arrayfile")
+ or die "Cannot write $arrayfile: $!";
+$array_out->print(join("\n", @behavior_chunks));
+$array_out->close();
+
+# Check that the records from the data file look reasonable.
+#
+# Some of the checks here cannot fail, because they check things that were
+# already checked before eval'ing the inputfile data. We check anyway, for
+# future-proofing and because the prior checks were against the stringified
+# form of the data and these checks are against the perl data structures that
+# we revivified, and the possibility of subtle corner cases that entails.
+#
+sub sanity_check_data
+{
+ my ($data) = @_;
+
+ # Verify that at the outermost layer the data file is an array reference
+ dataerror("Malformed input data: expected an array reference",
+ "The input file should start with a '[' and end with a ']'")
+ unless defined $data and ref($data) and ref($data) =~ m/ARRAY/;
+
+ # Verify that the data array contains only hash references
+ dataerror("Malformed input data: expected an array of hash references",
+ "The input file array should contain only records in perl hash " .
+ "reference '{...}' format")
+ if (grep { !ref($_) || ref($_) !~ m/HASH/ } @$data);
+
+ my @required_keys = qw(tagname taglabel);
+ my @flag_keys = qw(event_trigger_ok table_rewrite_ok display_rowcount
+ display_last_oid);
+ my @expected_keys = (@required_keys, @flag_keys);
+ my %expected_keys = map { $_ => 1 } @expected_keys;
+
+ # Check that each record in the data file contains the required
+ # fields. If we have only one required field (name or label) but
+ # not the other, we can use the one we have in the error message
+ # to help direct the user to the record which needs correcting.
+ foreach my $href (@$data)
+ {
+ my $preamble = "Malformed input data:";
+ if (defined $href->{taglabel} and length $href->{taglabel})
+ {
+ $preamble .= " Record for commandtag label '$href->{taglabel}':";
+ }
+ elsif (defined $href->{tagname} and length $href->{tagname})
+ {
+ $preamble .= " Record for commandtag name '$href->{tagname}':";
+ }
+
+ foreach my $key (@required_keys)
+ {
+ dataerror("$preamble missing '$key'",
+ "Each record in the data file should fields for @required_keys")
+ unless(exists $href->{$key});
+ dataerror("$preamble trivial value for '$key'")
+ unless(defined $href->{$key} and length $href->{$key});
+ dataerror("$preamble value of '$key' not uppercase: $href->{$key}")
+ unless($href->{$key} eq uc($href->{$key}));
+ }
+
+ foreach my $key (keys %$href)
+ {
+ dataerror("$preamble non-printable value for '$key'",
+ "Do not embed non-printable characters such as tabs, form-feeds or newlines")
+ if (grep { ord($_) < 0x20 } split(//, $href->{$key}));
+ dataerror("$preamble non-ASCII value for '$key'",
+ "Only printable 7-bit ASCII characters are allowed as commandtag data")
+ if (grep { ord($_) > 0x7F } split(//, $href->{$key}));
+ dataerror("$preamble unrecognized field '$key'",
+ "Recognized keys are @expected_keys")
+ unless $expected_keys{$key};
+ }
+
+ foreach my $flag (grep { exists $href->{$_} } @flag_keys)
+ {
+ my $val = $href->{$flag};
+ dataerror("$preamble empty or undefined value for '$flag'",
+ "Fields with no value should simply be omitted")
+ unless defined $val and length $val;
+ dataerror("$preamble false value need not be specified for '$flag'",
+ "Fields with false values should simply be omitted")
+ if ($val =~ m/^(?:false|f|0|off)$/i);
+ dataerror("$preamble unrecognized value for '$flag': '$val'")
+ unless ($val =~ m/^(?:true|t|1|on)$/i);
+ }
+ }
+}
+
+sub slurp_without_comments
+{
+ my ($path) = @_;
+ my @contents;
+ my $fh = IO::File->new($path) or die "Cannot read $path: $!";
+ {
+ while (my $line = <$fh>)
+ {
+ chomp($line); # Remove \n, if any
+ chomp($line); # Remove \r, if any
+ $line =~ s/#.*$//; # Remove comments
+ $line =~ s/\s+$//; # Remove trailing whitespace
+ push (@contents, $line) if length $line
+ }
+ }
+ $fh->close();
+ return join("\n", @contents);
+}
+
+sub usage
+{
+ die <<EOM;
+Usage: perl -I [directory of commandtag.dat] gencommandtag.pl [--headerdir/-h <path>]
+
+Options:
+ --inputfile Input commandtag data file (default <headerdir>/commandtag.dat)
+ --headerdir Output directory for commandtag.h (default '../../../include/utils')
+
+gencommandtag.pl generates the commandtag_enum.h and commandtag_behavior.h files
+from the specially formatted commandtag.dat data file.
+
+Report bugs to <pgsql-bugs\@lists.postgresql.org>.
+EOM
+}
diff --git a/src/backend/utils/misc/.gitignore b/src/backend/utils/misc/.gitignore
index 495b1aec76..e9cb531f4f 100644
--- a/src/backend/utils/misc/.gitignore
+++ b/src/backend/utils/misc/.gitignore
@@ -1 +1,2 @@
/guc-file.c
+/commandtag-stamp
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index 2397fc2453..389f71dd97 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -39,7 +39,11 @@ include $(top_srcdir)/src/backend/common.mk
# guc-file is compiled as part of guc
guc.o: guc-file.c
+all: distprep
+
# Note: guc-file.c is not deleted by 'make clean',
# since we want to ship it in distribution tarballs.
clean:
@rm -f lex.yy.c
+
+maintainer-clean: clean
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index b675575c31..00e8065342 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -281,7 +281,7 @@ void
PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan)
{
@@ -289,10 +289,12 @@ PortalDefineQuery(Portal portal,
AssertState(portal->status == PORTAL_NEW);
AssertArg(sourceText != NULL);
- AssertArg(commandTag != NULL || stmts == NIL);
+ AssertArg(commandTag != COMMANDTAG_UNKNOWN || stmts == NIL);
portal->prepStmtName = prepStmtName;
portal->sourceText = sourceText;
+ portal->qc.commandTag = commandTag;
+ portal->qc.nprocessed = 0;
portal->commandTag = commandTag;
portal->stmts = stmts;
portal->cplan = cplan;
diff --git a/src/include/Makefile b/src/include/Makefile
index c557375ae3..fadbd737c9 100644
--- a/src/include/Makefile
+++ b/src/include/Makefile
@@ -44,6 +44,8 @@ install: all installdirs
$(INSTALL_DATA) pg_config.h '$(DESTDIR)$(includedir_server)'
$(INSTALL_DATA) pg_config_ext.h '$(DESTDIR)$(includedir_server)'
$(INSTALL_DATA) pg_config_os.h '$(DESTDIR)$(includedir_server)'
+ $(INSTALL_DATA) utils/commandtag_enum.h '$(DESTDIR)$(includedir_server)/utils'
+ $(INSTALL_DATA) utils/commandtag_behavior.h '$(DESTDIR)$(includedir_server)/utils'
$(INSTALL_DATA) utils/errcodes.h '$(DESTDIR)$(includedir_server)/utils'
$(INSTALL_DATA) utils/fmgroids.h '$(DESTDIR)$(includedir_server)/utils'
$(INSTALL_DATA) utils/fmgrprotos.h '$(DESTDIR)$(includedir_server)/utils'
@@ -77,7 +79,7 @@ uninstall:
clean:
- rm -f utils/fmgroids.h utils/fmgrprotos.h utils/errcodes.h utils/header-stamp
+ rm -f utils/commandtag_enum.h utils/commandtag_behavior.h utils/fmgroids.h utils/fmgrprotos.h utils/errcodes.h utils/header-stamp
rm -f parser/gram.h storage/lwlocknames.h utils/probes.h
rm -f catalog/schemapg.h catalog/pg_*_d.h catalog/header-stamp
diff --git a/src/include/commands/createas.h b/src/include/commands/createas.h
index 7743851a38..5615b5ecac 100644
--- a/src/include/commands/createas.h
+++ b/src/include/commands/createas.h
@@ -22,7 +22,8 @@
extern ObjectAddress ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
- ParamListInfo params, QueryEnvironment *queryEnv, char *completionTag);
+ ParamListInfo params, QueryEnvironment *queryEnv,
+ QueryCompletion *qc);
extern int GetIntoRelEFlags(IntoClause *intoClause);
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index faa2958b89..5123c4b850 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -25,7 +25,7 @@ typedef struct EventTriggerData
NodeTag type;
const char *event; /* event name */
Node *parsetree; /* parse tree */
- const char *tag; /* command tag */
+ CommandTag tag;
} EventTriggerData;
#define AT_REWRITE_ALTER_PERSISTENCE 0x01
diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h
index 6bdb7ca258..3ea4f5c80b 100644
--- a/src/include/commands/matview.h
+++ b/src/include/commands/matview.h
@@ -24,7 +24,7 @@
extern void SetMatViewPopulatedState(Relation relation, bool newstate);
extern ObjectAddress ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag);
+ ParamListInfo params, QueryCompletion *qc);
extern DestReceiver *CreateTransientRelDestReceiver(Oid oid);
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index 4ecc1a2ecd..41bb7c395b 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -22,8 +22,7 @@
extern void PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo params,
bool isTopLevel);
-extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
- char *completionTag);
+extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest, QueryCompletion *qc);
extern void PerformPortalClose(const char *name);
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index a0509e1f33..4fcf2406c1 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -40,7 +40,7 @@ extern void PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
extern void ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void DeallocateQuery(DeallocateStmt *stmt);
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into,
ExplainState *es, const char *queryString,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index da0706add5..2c3ae6b603 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -27,6 +27,7 @@
#include "nodes/primnodes.h"
#include "nodes/value.h"
#include "partitioning/partdefs.h"
+#include "utils/commandtag.h"
typedef enum OverridingKind
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 35bce731a1..dd699b0993 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -68,7 +68,7 @@
#define DEST_H
#include "executor/tuptable.h"
-
+#include "utils/commandtag.h"
/* buffer size to use for command completion tags */
#define COMPLETION_TAG_BUFSIZE 64
@@ -134,9 +134,10 @@ extern PGDLLIMPORT DestReceiver *None_Receiver; /* permanent receiver for
/* The primary destination management functions */
-extern void BeginCommand(const char *commandTag, CommandDest dest);
+extern void BeginCommand(CommandTag commandTag, CommandDest dest);
extern DestReceiver *CreateDestReceiver(CommandDest dest);
-extern void EndCommand(const char *commandTag, CommandDest dest);
+extern void EndCommand(const QueryCompletion *qc, CommandDest dest,
+ bool force_undecorated_output);
/* Additional functions that go with destination management, more or less. */
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index 4ad6324e2d..437642cc72 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.h
@@ -35,7 +35,7 @@ extern void PortalSetResultFormat(Portal portal, int nFormats,
extern bool PortalRun(Portal portal, long count, bool isTopLevel,
bool run_once, DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletion *qc);
extern uint64 PortalRunFetch(Portal portal,
FetchDirection fdirection,
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index a551e08cb8..8781ac7d38 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -15,6 +15,7 @@
#define UTILITY_H
#include "tcop/tcopprot.h"
+#include "utils/commandtag.h"
typedef enum
{
@@ -71,17 +72,17 @@ typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
const char *queryString, ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void ProcessUtilityForAlterTable(Node *stmt,
AlterTableUtilityContext *context);
@@ -92,7 +93,13 @@ extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
extern Query *UtilityContainsQuery(Node *parsetree);
-extern const char *CreateCommandTag(Node *parsetree);
+extern CommandTag CreateCommandTag(Node *parsetree);
+
+static inline const char *
+CreateCommandName(Node *parsetree)
+{
+ return GetCommandTagName(CreateCommandTag(parsetree));
+}
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
diff --git a/src/include/utils/.gitignore b/src/include/utils/.gitignore
index 05cfa7a8d6..9bc600aacc 100644
--- a/src/include/utils/.gitignore
+++ b/src/include/utils/.gitignore
@@ -1,3 +1,5 @@
+/commandtag_behavior.h
+/commandtag_enum.h
/fmgroids.h
/fmgrprotos.h
/probes.h
diff --git a/src/include/utils/commandtag.dat b/src/include/utils/commandtag.dat
new file mode 100644
index 0000000000..a79ee50e00
--- /dev/null
+++ b/src/include/utils/commandtag.dat
@@ -0,0 +1,938 @@
+#----------------------------------------------------------------------
+#
+# commandtag.dat
+# Data file from which commandtag_enum.h and commandtag_behavior.h
+# are generated
+#
+# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/backend/utils/commandtag.dat
+#
+#----------------------------------------------------------------------
+
+# gencommandtag.pl will sort these records, so there is no requirement
+# that they be correctly sorted here, but by convention, we keep these
+# in sorted order by tagname.
+#
+# The 'taglabel' field is typically just a repeat of the tagname but
+# with spaces converted to underscores. It serves as the label within
+# the generated CommandTag enum. For weirder names, like ???, the
+# label does not match, for obvious reasons.
+#
+# The following boolean fields specify whether event trigger support
+# exists for the given command tag:
+#
+# event_trigger_ok
+# table_rewrite_ok
+#
+# And these two boolean fields specify the formatting that EndCommand
+# should send back to the client for the command tag:
+#
+# display_rowcount
+# display_last_oid
+#
+# For these four boolean fields, the default is false. Only specify
+# a value for records where they are true.
+[
+{
+ taglabel => 'UNKNOWN',
+ tagname => '???',
+},
+{
+ taglabel => 'ALTER_ACCESS_METHOD',
+ tagname => 'ALTER ACCESS METHOD',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_AGGREGATE',
+ tagname => 'ALTER AGGREGATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_CAST',
+ tagname => 'ALTER CAST',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_COLLATION',
+ tagname => 'ALTER COLLATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_CONSTRAINT',
+ tagname => 'ALTER CONSTRAINT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_CONVERSION',
+ tagname => 'ALTER CONVERSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_DATABASE',
+ tagname => 'ALTER DATABASE',
+},
+{
+ taglabel => 'ALTER_DEFAULT_PRIVILEGES',
+ tagname => 'ALTER DEFAULT PRIVILEGES',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_DOMAIN',
+ tagname => 'ALTER DOMAIN',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_EVENT_TRIGGER',
+ tagname => 'ALTER EVENT TRIGGER',
+},
+{
+ taglabel => 'ALTER_EXTENSION',
+ tagname => 'ALTER EXTENSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_FOREIGN_DATA_WRAPPER',
+ tagname => 'ALTER FOREIGN DATA WRAPPER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_FOREIGN_TABLE',
+ tagname => 'ALTER FOREIGN TABLE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_FUNCTION',
+ tagname => 'ALTER FUNCTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_INDEX',
+ tagname => 'ALTER INDEX',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_LANGUAGE',
+ tagname => 'ALTER LANGUAGE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_LARGE_OBJECT',
+ tagname => 'ALTER LARGE OBJECT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_MATERIALIZED_VIEW',
+ tagname => 'ALTER MATERIALIZED VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_OPERATOR',
+ tagname => 'ALTER OPERATOR',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_OPERATOR_CLASS',
+ tagname => 'ALTER OPERATOR CLASS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_OPERATOR_FAMILY',
+ tagname => 'ALTER OPERATOR FAMILY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_POLICY',
+ tagname => 'ALTER POLICY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_PROCEDURE',
+ tagname => 'ALTER PROCEDURE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_PUBLICATION',
+ tagname => 'ALTER PUBLICATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_ROLE',
+ tagname => 'ALTER ROLE',
+},
+{
+ taglabel => 'ALTER_ROUTINE',
+ tagname => 'ALTER ROUTINE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_RULE',
+ tagname => 'ALTER RULE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_SCHEMA',
+ tagname => 'ALTER SCHEMA',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_SEQUENCE',
+ tagname => 'ALTER SEQUENCE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_SERVER',
+ tagname => 'ALTER SERVER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_STATISTICS',
+ tagname => 'ALTER STATISTICS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_SUBSCRIPTION',
+ tagname => 'ALTER SUBSCRIPTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_SYSTEM',
+ tagname => 'ALTER SYSTEM',
+},
+{
+ taglabel => 'ALTER_TABLE',
+ tagname => 'ALTER TABLE',
+ event_trigger_ok => 'true',
+ table_rewrite_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TABLESPACE',
+ tagname => 'ALTER TABLESPACE',
+},
+{
+ taglabel => 'ALTER_TEXT_SEARCH_CONFIGURATION',
+ tagname => 'ALTER TEXT SEARCH CONFIGURATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TEXT_SEARCH_DICTIONARY',
+ tagname => 'ALTER TEXT SEARCH DICTIONARY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TEXT_SEARCH_PARSER',
+ tagname => 'ALTER TEXT SEARCH PARSER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TEXT_SEARCH_TEMPLATE',
+ tagname => 'ALTER TEXT SEARCH TEMPLATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TRANSFORM',
+ tagname => 'ALTER TRANSFORM',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TRIGGER',
+ tagname => 'ALTER TRIGGER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TYPE',
+ tagname => 'ALTER TYPE',
+ event_trigger_ok => 'true',
+ table_rewrite_ok => 'true',
+},
+{
+ taglabel => 'ALTER_USER_MAPPING',
+ tagname => 'ALTER USER MAPPING',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_VIEW',
+ tagname => 'ALTER VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ANALYZE',
+ tagname => 'ANALYZE',
+},
+{
+ taglabel => 'BEGIN',
+ tagname => 'BEGIN',
+},
+{
+ taglabel => 'CALL',
+ tagname => 'CALL',
+},
+{
+ taglabel => 'CHECKPOINT',
+ tagname => 'CHECKPOINT',
+},
+{
+ taglabel => 'CLOSE',
+ tagname => 'CLOSE',
+},
+{
+ taglabel => 'CLOSE_CURSOR',
+ tagname => 'CLOSE CURSOR',
+},
+{
+ taglabel => 'CLOSE_CURSOR_ALL',
+ tagname => 'CLOSE CURSOR ALL',
+},
+{
+ taglabel => 'CLUSTER',
+ tagname => 'CLUSTER',
+},
+{
+ taglabel => 'COMMENT',
+ tagname => 'COMMENT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'COMMIT',
+ tagname => 'COMMIT',
+},
+{
+ taglabel => 'COMMIT_PREPARED',
+ tagname => 'COMMIT PREPARED',
+},
+{
+ taglabel => 'COPY',
+ tagname => 'COPY',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'COPY_FROM',
+ tagname => 'COPY FROM',
+},
+{
+ taglabel => 'CREATE_ACCESS_METHOD',
+ tagname => 'CREATE ACCESS METHOD',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_AGGREGATE',
+ tagname => 'CREATE AGGREGATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_CAST',
+ tagname => 'CREATE CAST',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_COLLATION',
+ tagname => 'CREATE COLLATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_CONSTRAINT',
+ tagname => 'CREATE CONSTRAINT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_CONVERSION',
+ tagname => 'CREATE CONVERSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_DATABASE',
+ tagname => 'CREATE DATABASE',
+},
+{
+ taglabel => 'CREATE_DOMAIN',
+ tagname => 'CREATE DOMAIN',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_EVENT_TRIGGER',
+ tagname => 'CREATE EVENT TRIGGER',
+},
+{
+ taglabel => 'CREATE_EXTENSION',
+ tagname => 'CREATE EXTENSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_FOREIGN_DATA_WRAPPER',
+ tagname => 'CREATE FOREIGN DATA WRAPPER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_FOREIGN_TABLE',
+ tagname => 'CREATE FOREIGN TABLE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_FUNCTION',
+ tagname => 'CREATE FUNCTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_INDEX',
+ tagname => 'CREATE INDEX',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_LANGUAGE',
+ tagname => 'CREATE LANGUAGE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_MATERIALIZED_VIEW',
+ tagname => 'CREATE MATERIALIZED VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_OPERATOR',
+ tagname => 'CREATE OPERATOR',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_OPERATOR_CLASS',
+ tagname => 'CREATE OPERATOR CLASS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_OPERATOR_FAMILY',
+ tagname => 'CREATE OPERATOR FAMILY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_POLICY',
+ tagname => 'CREATE POLICY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_PROCEDURE',
+ tagname => 'CREATE PROCEDURE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_PUBLICATION',
+ tagname => 'CREATE PUBLICATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_ROLE',
+ tagname => 'CREATE ROLE',
+},
+{
+ taglabel => 'CREATE_ROUTINE',
+ tagname => 'CREATE ROUTINE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_RULE',
+ tagname => 'CREATE RULE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_SCHEMA',
+ tagname => 'CREATE SCHEMA',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_SEQUENCE',
+ tagname => 'CREATE SEQUENCE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_SERVER',
+ tagname => 'CREATE SERVER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_STATISTICS',
+ tagname => 'CREATE STATISTICS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_SUBSCRIPTION',
+ tagname => 'CREATE SUBSCRIPTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TABLE',
+ tagname => 'CREATE TABLE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TABLE_AS',
+ tagname => 'CREATE TABLE AS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TABLESPACE',
+ tagname => 'CREATE TABLESPACE',
+},
+{
+ taglabel => 'CREATE_TEXT_SEARCH_CONFIGURATION',
+ tagname => 'CREATE TEXT SEARCH CONFIGURATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TEXT_SEARCH_DICTIONARY',
+ tagname => 'CREATE TEXT SEARCH DICTIONARY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TEXT_SEARCH_PARSER',
+ tagname => 'CREATE TEXT SEARCH PARSER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TEXT_SEARCH_TEMPLATE',
+ tagname => 'CREATE TEXT SEARCH TEMPLATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TRANSFORM',
+ tagname => 'CREATE TRANSFORM',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TRIGGER',
+ tagname => 'CREATE TRIGGER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TYPE',
+ tagname => 'CREATE TYPE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_USER_MAPPING',
+ tagname => 'CREATE USER MAPPING',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_VIEW',
+ tagname => 'CREATE VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DEALLOCATE',
+ tagname => 'DEALLOCATE',
+},
+{
+ taglabel => 'DEALLOCATE_ALL',
+ tagname => 'DEALLOCATE ALL',
+},
+{
+ taglabel => 'DECLARE_CURSOR',
+ tagname => 'DECLARE CURSOR',
+},
+{
+ taglabel => 'DELETE',
+ tagname => 'DELETE',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'DISCARD',
+ tagname => 'DISCARD',
+},
+{
+ taglabel => 'DISCARD_ALL',
+ tagname => 'DISCARD ALL',
+},
+{
+ taglabel => 'DISCARD_PLANS',
+ tagname => 'DISCARD PLANS',
+},
+{
+ taglabel => 'DISCARD_SEQUENCES',
+ tagname => 'DISCARD SEQUENCES',
+},
+{
+ taglabel => 'DISCARD_TEMP',
+ tagname => 'DISCARD TEMP',
+},
+{
+ taglabel => 'DO',
+ tagname => 'DO',
+},
+{
+ taglabel => 'DROP_ACCESS_METHOD',
+ tagname => 'DROP ACCESS METHOD',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_AGGREGATE',
+ tagname => 'DROP AGGREGATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_CAST',
+ tagname => 'DROP CAST',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_COLLATION',
+ tagname => 'DROP COLLATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_CONSTRAINT',
+ tagname => 'DROP CONSTRAINT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_CONVERSION',
+ tagname => 'DROP CONVERSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_DATABASE',
+ tagname => 'DROP DATABASE',
+},
+{
+ taglabel => 'DROP_DOMAIN',
+ tagname => 'DROP DOMAIN',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_EVENT_TRIGGER',
+ tagname => 'DROP EVENT TRIGGER',
+},
+{
+ taglabel => 'DROP_EXTENSION',
+ tagname => 'DROP EXTENSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_FOREIGN_DATA_WRAPPER',
+ tagname => 'DROP FOREIGN DATA WRAPPER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_FOREIGN_TABLE',
+ tagname => 'DROP FOREIGN TABLE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_FUNCTION',
+ tagname => 'DROP FUNCTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_INDEX',
+ tagname => 'DROP INDEX',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_LANGUAGE',
+ tagname => 'DROP LANGUAGE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_MATERIALIZED_VIEW',
+ tagname => 'DROP MATERIALIZED VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_OPERATOR',
+ tagname => 'DROP OPERATOR',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_OPERATOR_CLASS',
+ tagname => 'DROP OPERATOR CLASS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_OPERATOR_FAMILY',
+ tagname => 'DROP OPERATOR FAMILY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_OWNED',
+ tagname => 'DROP OWNED',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_POLICY',
+ tagname => 'DROP POLICY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_PROCEDURE',
+ tagname => 'DROP PROCEDURE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_PUBLICATION',
+ tagname => 'DROP PUBLICATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_REPLICATION_SLOT',
+ tagname => 'DROP REPLICATION SLOT',
+},
+{
+ taglabel => 'DROP_ROLE',
+ tagname => 'DROP ROLE',
+},
+{
+ taglabel => 'DROP_ROUTINE',
+ tagname => 'DROP ROUTINE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_RULE',
+ tagname => 'DROP RULE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_SCHEMA',
+ tagname => 'DROP SCHEMA',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_SEQUENCE',
+ tagname => 'DROP SEQUENCE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_SERVER',
+ tagname => 'DROP SERVER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_STATISTICS',
+ tagname => 'DROP STATISTICS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_SUBSCRIPTION',
+ tagname => 'DROP SUBSCRIPTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TABLE',
+ tagname => 'DROP TABLE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TABLESPACE',
+ tagname => 'DROP TABLESPACE',
+},
+{
+ taglabel => 'DROP_TEXT_SEARCH_CONFIGURATION',
+ tagname => 'DROP TEXT SEARCH CONFIGURATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TEXT_SEARCH_DICTIONARY',
+ tagname => 'DROP TEXT SEARCH DICTIONARY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TEXT_SEARCH_PARSER',
+ tagname => 'DROP TEXT SEARCH PARSER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TEXT_SEARCH_TEMPLATE',
+ tagname => 'DROP TEXT SEARCH TEMPLATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TRANSFORM',
+ tagname => 'DROP TRANSFORM',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TRIGGER',
+ tagname => 'DROP TRIGGER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TYPE',
+ tagname => 'DROP TYPE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_USER_MAPPING',
+ tagname => 'DROP USER MAPPING',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_VIEW',
+ tagname => 'DROP VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'EXECUTE',
+ tagname => 'EXECUTE',
+},
+{
+ taglabel => 'EXPLAIN',
+ tagname => 'EXPLAIN',
+},
+{
+ taglabel => 'FETCH',
+ tagname => 'FETCH',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'GRANT',
+ tagname => 'GRANT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'GRANT_ROLE',
+ tagname => 'GRANT ROLE',
+},
+{
+ taglabel => 'IMPORT_FOREIGN_SCHEMA',
+ tagname => 'IMPORT FOREIGN SCHEMA',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'INSERT',
+ tagname => 'INSERT',
+ display_last_oid => 'true',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'LISTEN',
+ tagname => 'LISTEN',
+},
+{
+ taglabel => 'LOAD',
+ tagname => 'LOAD',
+},
+{
+ taglabel => 'LOCK_TABLE',
+ tagname => 'LOCK TABLE',
+},
+{
+ taglabel => 'MOVE',
+ tagname => 'MOVE',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'NOTIFY',
+ tagname => 'NOTIFY',
+},
+{
+ taglabel => 'PREPARE',
+ tagname => 'PREPARE',
+},
+{
+ taglabel => 'PREPARE_TRANSACTION',
+ tagname => 'PREPARE TRANSACTION',
+},
+{
+ taglabel => 'REASSIGN_OWNED',
+ tagname => 'REASSIGN OWNED',
+},
+{
+ taglabel => 'REFRESH_MATERIALIZED_VIEW',
+ tagname => 'REFRESH MATERIALIZED VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'REINDEX',
+ tagname => 'REINDEX',
+},
+{
+ taglabel => 'RELEASE',
+ tagname => 'RELEASE',
+},
+{
+ taglabel => 'RESET',
+ tagname => 'RESET',
+},
+{
+ taglabel => 'REVOKE',
+ tagname => 'REVOKE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'REVOKE_ROLE',
+ tagname => 'REVOKE ROLE',
+},
+{
+ taglabel => 'ROLLBACK',
+ tagname => 'ROLLBACK',
+},
+{
+ taglabel => 'ROLLBACK_PREPARED',
+ tagname => 'ROLLBACK PREPARED',
+},
+{
+ taglabel => 'SAVEPOINT',
+ tagname => 'SAVEPOINT',
+},
+{
+ taglabel => 'SECURITY_LABEL',
+ tagname => 'SECURITY LABEL',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'SELECT',
+ tagname => 'SELECT',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'SELECT_FOR_KEY_SHARE',
+ tagname => 'SELECT FOR KEY SHARE',
+},
+{
+ taglabel => 'SELECT_FOR_NO_KEY_UPDATE',
+ tagname => 'SELECT FOR NO KEY UPDATE',
+},
+{
+ taglabel => 'SELECT_FOR_SHARE',
+ tagname => 'SELECT FOR SHARE',
+},
+{
+ taglabel => 'SELECT_FOR_UPDATE',
+ tagname => 'SELECT FOR UPDATE',
+},
+{
+ taglabel => 'SELECT_INTO',
+ tagname => 'SELECT INTO',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'SET',
+ tagname => 'SET',
+},
+{
+ taglabel => 'SET_CONSTRAINTS',
+ tagname => 'SET CONSTRAINTS',
+},
+{
+ taglabel => 'SHOW',
+ tagname => 'SHOW',
+},
+{
+ taglabel => 'START_TRANSACTION',
+ tagname => 'START TRANSACTION',
+},
+{
+ taglabel => 'TRUNCATE_TABLE',
+ tagname => 'TRUNCATE TABLE',
+},
+{
+ taglabel => 'UNLISTEN',
+ tagname => 'UNLISTEN',
+},
+{
+ taglabel => 'UPDATE',
+ tagname => 'UPDATE',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'VACUUM',
+ tagname => 'VACUUM',
+},
+]
diff --git a/src/include/utils/commandtag.h b/src/include/utils/commandtag.h
new file mode 100644
index 0000000000..ea9b2c682a
--- /dev/null
+++ b/src/include/utils/commandtag.h
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * commandtag.h
+ * Declarations for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/commandtag.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMMANDTAG_H
+#define COMMANDTAG_H
+
+#include "utils/commandtag_enum.h"
+
+typedef struct CommandTagBehavior {
+ const char *name;
+ const bool display_last_oid;
+ const bool display_rowcount;
+ const bool event_trigger_ok;
+ const bool table_rewrite_ok;
+} CommandTagBehavior;
+
+extern const CommandTagBehavior tag_behavior[];
+
+typedef struct QueryCompletion
+{
+ CommandTag commandTag;
+ uint64 nprocessed;
+} QueryCompletion;
+
+static inline void
+SetQueryCompletion(QueryCompletion *qc, CommandTag commandTag, uint64 nprocessed)
+{
+ qc->commandTag = commandTag;
+ qc->nprocessed = nprocessed;
+}
+
+static inline void
+CopyQueryCompletion(QueryCompletion *dst, const QueryCompletion *src)
+{
+ dst->commandTag = src->commandTag;
+ dst->nprocessed = src->nprocessed;
+}
+
+extern void InitializeQueryCompletion(QueryCompletion *qc);
+extern const char *GetCommandTagName(CommandTag commandTag);
+extern bool command_tag_display_last_oid(CommandTag commandTag);
+extern bool command_tag_display_rowcount(CommandTag commandTag);
+extern bool command_tag_event_trigger_ok(CommandTag commandTag);
+extern bool command_tag_table_rewrite_ok(CommandTag commandTag);
+extern CommandTag GetCommandTagEnum(const char *tagname);
+extern void PreventCommandTagIfReadOnly(CommandTag commandTag);
+extern void PreventCommandTagIfParallelMode(CommandTag commandTag);
+extern void PreventCommandTagDuringRecovery(CommandTag commandTag);
+
+#endif /* COMMANDTAG_H */
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index 6c3ff81ba3..bc8ce48061 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -28,8 +28,7 @@ typedef struct
{
Oid fnoid; /* function to be called */
char enabled; /* as SESSION_REPLICATION_ROLE_* */
- int ntags; /* number of command tags */
- char **tag; /* command tags in SORTED order */
+ Bitmapset *tagset; /* command tags, or NULL if empty */
} EventTriggerCacheItem;
extern List *EventCacheLookup(EventTriggerEvent event);
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index e48661ebec..b2900307ad 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -18,6 +18,7 @@
#include "access/tupdesc.h"
#include "lib/ilist.h"
#include "nodes/params.h"
+#include "utils/commandtag.h"
#include "utils/queryenvironment.h"
/* Forward declaration, to avoid including parsenodes.h here */
@@ -95,7 +96,7 @@ typedef struct CachedPlanSource
int magic; /* should equal CACHEDPLANSOURCE_MAGIC */
struct RawStmt *raw_parse_tree; /* output of raw_parser(), or NULL */
const char *query_string; /* source text of query */
- const char *commandTag; /* command tag (a constant!), or NULL */
+ CommandTag commandTag;
Oid *param_types; /* array of parameter type OIDs, or NULL */
int num_params; /* length of param_types array */
ParserSetupHook parserSetup; /* alternative parameter spec method */
@@ -186,10 +187,10 @@ extern void ResetPlanCache(void);
extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern void CompleteCachedPlan(CachedPlanSource *plansource,
List *querytree_list,
MemoryContext querytree_context,
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 0b69433722..b398ae45a3 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -48,6 +48,7 @@
#include "datatype/timestamp.h"
#include "executor/execdesc.h"
+#include "utils/commandtag.h"
#include "utils/plancache.h"
#include "utils/resowner.h"
@@ -132,7 +133,8 @@ typedef struct PortalData
/* The query or queries the portal will execute */
const char *sourceText; /* text of query (as of 8.4, never NULL) */
- const char *commandTag; /* command tag for original query */
+ CommandTag commandTag; /* command tag for original query */
+ QueryCompletion qc; /* command completion data for executed query */
List *stmts; /* list of PlannedStmts */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
@@ -227,7 +229,7 @@ extern Portal GetPortalByName(const char *name);
extern void PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan);
extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 5acf604f63..e165790d66 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -41,6 +41,7 @@
#include "tcop/utility.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/commandtag.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
@@ -1473,7 +1474,7 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
case PLPGSQL_PROMISE_TG_TAG:
if (estate->evtrigdata == NULL)
elog(ERROR, "event trigger promise is not in an event trigger function");
- assign_text_var(estate, var, estate->evtrigdata->tag);
+ assign_text_var(estate, var, GetCommandTagName(estate->evtrigdata->tag));
break;
default:
@@ -4115,10 +4116,9 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
* tree(s), since those are the result of rewriting and could have
* been transmogrified into something else entirely.
*/
- if (plansource->commandTag &&
- (strcmp(plansource->commandTag, "INSERT") == 0 ||
- strcmp(plansource->commandTag, "UPDATE") == 0 ||
- strcmp(plansource->commandTag, "DELETE") == 0))
+ if (plansource->commandTag == COMMANDTAG_INSERT ||
+ plansource->commandTag == COMMANDTAG_UPDATE ||
+ plansource->commandTag == COMMANDTAG_DELETE)
{
stmt->mod_stmt = true;
break;
diff --git a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
index e1629ec618..b7bdb88ce7 100644
--- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
+++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
@@ -74,7 +74,7 @@ get_command_tag(PG_FUNCTION_ARGS)
if (!cmd->parsetree)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(cstring_to_text(CreateCommandTag(cmd->parsetree)));
+ PG_RETURN_TEXT_P(cstring_to_text(CreateCommandName(cmd->parsetree)));
}
/*
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 8412ef298e..5afc83498f 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -558,6 +558,7 @@ sub GenerateFiles
chdir('src/backend/utils');
my $pg_proc_dat = '../../../src/include/catalog/pg_proc.dat';
+ my $commandtag_dat = '../../../src/include/utils/commandtag.dat';
if ( IsNewer('fmgr-stamp', 'Gen_fmgrtab.pl')
|| IsNewer('fmgr-stamp', '../catalog/Catalog.pm')
|| IsNewer('fmgr-stamp', $pg_proc_dat)
@@ -570,6 +571,17 @@ sub GenerateFiles
|| confess "Could not touch fmgr-stamp";
close($f);
}
+ if ( IsNewer('commandtag-stamp', 'gencommandtag.pl')
+ || IsNewer('commandtag-stamp', 'DataFile.pm')
+ || IsNewer('commandtag-stamp', $commandtag_dat))
+ {
+ system(
+ "perl -I ../../../src/include/utils gencommandtag.pl --headerdir=../../../src/include/utils --sourcedir=. --inputfile=$commandtag_dat"
+ );
+ open(my $f, '>', 'commandtag-stamp')
+ || confess "Could not touch commandtag-stamp";
+ close($f);
+ }
chdir('../../..');
if (IsNewer(
--
2.21.1 (Apple Git-122.3)
Hi Mark,
On Wed, Feb 19, 2020 at 10:40 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
This would only make sense to me if the string held in $_ had already been checked for safety, but Catalog.pm does very little to verify that the string is safe to eval. The assumption here, so far as I can infer, is that we don’t embed anything dangerous in our .dat files, so this should be ok. That may be true for the moment, but I can imagine a day when we start embedding perl functions as quoted text inside a data file, or shell commands as text which look enough like perl for eval() to be able to execute them. Developers who edit these .dat files and mess up the quoting, and then rerun ‘make’ to get the new .c and .h files generated, may not like the side effects. Perhaps I’m being overly paranoid….
The use case for that seems slim. However, at a brief glance your
module seems more robust in other ways.
Rather than add more code generation logic based on the design of Catalog.pm, I wrote a perl based data file parser that parses .dat files and returns vivified perl data, as Catalog.pm does, but with much stricter parsing logic to make certain nothing dangerous gets eval()ed. I put the new module in DataFile.pm.
[...]
The new parser is more flexible about the structure of the data, which seems good to me for making it easier to add or modify data files in the future. The new parser does not yet have a means of hacking up the data to add autogenerated fields and such that Catalog.pm does, but I think a more clean break between parsing and autovivifying fields would be good anyway.
Separation of concerns sounds like a good idea, but I've not fully
thought it through. For one advantage, I think it might be nicer to
have indexing.dat and toasting.dat instead of having to dig the info
out of C macros. This was evident while recently experimenting with
generating catcache control data.
As for the patch, I have not done a full review, but I have some
comments based on a light read-through:
utils/Makefile:
+# location of commandtag.dat
+headerdir = $(top_srcdir)/src/include/utils
This variable name is too generic for what the comment says it is. A
better abstraction, if we want one, would be the full path to the
commandtag input file. The other script invocations in this Makefile
don't do it this way, but that's a separate patch.
+# location to write generated headers
+sourcedir = $(top_srcdir)/src/backend/utils
Calling the output the source is bound to confuse people. The comment
implies all generated headers, not just the ones introduced by the
patch. I would just output to the current directory (i.e. have an
--output option with a default empty string). Also, if we want to
output somewhere else, I would imagine it'd be under the top builddir,
not srcdir.
+$(PERL) -I $(top_srcdir)/src/include/utils $<
--headerdir=$(headerdir) --sourcedir=$(sourcedir)
--inputfile=$(headerdir)/commandtag.dat
1. headerdir is entirely unused by the script
2. We can default to working dir for the output as mentioned above
3. -I $(top_srcdir)/src/include/utils is supposed to point to the dir
containing DataFile.pm, but since gencommandtag.pl has "use lib..."
it's probably not needed here. I don't recall why we keep the "-I"
elsewhere. (ditto in Solution.pm)
I'm thinking it would look something like this:
+$(PERL) $< --inputfile=$(top_srcdir)/src/include/utils/commandtag.dat
--
utils/misc/Makefile
+all: distprep
+
# Note: guc-file.c is not deleted by 'make clean',
# since we want to ship it in distribution tarballs.
clean:
@rm -f lex.yy.c
+
+maintainer-clean: clean
Seems non-functional.
--
DataFiles.pm
I haven't studied this in detail, but I'll suggest that if this meant
to have wider application, maybe it should live in src/tools ?
I'm not familiar with using different IO routines depending on the OS
-- what's the benefit of that?
--
gencommandtag.pl
slurp_without_comments() is unused.
sanity_check_data() seems longer than the main body of the script
(minus header boilerplate), and I wonder if we can pare it down some.
For one, I have difficulty imagining anyone would accidentally type an
unprintable or non-ascii character in a command tag and somehow not
realize it. For another, duplicating checks that were done earlier
seems like a maintenance headache.
dataerror() is defined near the top, but other functions are defined
at the bottom.
+# Generate all output internally before outputting anything, to avoid
+# partially overwriting generated files under error conditions
My personal preference is, having this as a design requirement
sacrifices readability for unclear gain, especially since a "chunk"
also includes things like header boilerplate. That said, the script is
also short enough that it doesn't make a huge difference either way.
Speaking of boilerplate, it's better for readability to separate that
from actual code such as:
typedef enum CommandTag
{
#define FIRST_COMMANDTAG COMMANDTAG_$sorted[0]->{taglabel})
--
tcop/dest.c
+ * We no longer display LastOid, but to preserve the wire protocol,
+ * we write InvalidOid where the LastOid used to be written. For
+ * efficiency in the snprintf(), hard-code InvalidOid as zero.
Hmm, is hard-coding zero going to make any difference here?
--
John Naylor https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Feb 19, 2020, at 3:31 AM, John Naylor <john.naylor@2ndquadrant.com> wrote:
Hi Mark,
Hi John, thanks for reviewing!
On Wed, Feb 19, 2020 at 10:40 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:This would only make sense to me if the string held in $_ had already been checked for safety, but Catalog.pm does very little to verify that the string is safe to eval. The assumption here, so far as I can infer, is that we don’t embed anything dangerous in our .dat files, so this should be ok. That may be true for the moment, but I can imagine a day when we start embedding perl functions as quoted text inside a data file, or shell commands as text which look enough like perl for eval() to be able to execute them. Developers who edit these .dat files and mess up the quoting, and then rerun ‘make’ to get the new .c and .h files generated, may not like the side effects. Perhaps I’m being overly paranoid….
The use case for that seems slim. However, at a brief glance your
module seems more robust in other ways.Rather than add more code generation logic based on the design of Catalog.pm, I wrote a perl based data file parser that parses .dat files and returns vivified perl data, as Catalog.pm does, but with much stricter parsing logic to make certain nothing dangerous gets eval()ed. I put the new module in DataFile.pm.
[...]
The new parser is more flexible about the structure of the data, which seems good to me for making it easier to add or modify data files in the future. The new parser does not yet have a means of hacking up the data to add autogenerated fields and such that Catalog.pm does, but I think a more clean break between parsing and autovivifying fields would be good anyway.Separation of concerns sounds like a good idea, but I've not fully
thought it through. For one advantage, I think it might be nicer to
have indexing.dat and toasting.dat instead of having to dig the info
out of C macros. This was evident while recently experimenting with
generating catcache control data.
I guess you mean macros DECLARE_UNIQUE_INDEX and DECLARE_TOAST. I don’t mind converting that to .dat files, though I’m mindful of Tom’s concern expressed early in this thread about the amount of code churn. Is there sufficient demand for refactoring this stuff? There are more reasons in the conversation below to refactor the perl modules and code generation scripts.
As for the patch, I have not done a full review, but I have some
comments based on a light read-through:utils/Makefile:
+# location of commandtag.dat +headerdir = $(top_srcdir)/src/include/utilsThis variable name is too generic for what the comment says it is. A
better abstraction, if we want one, would be the full path to the
commandtag input file. The other script invocations in this Makefile
don't do it this way, but that's a separate patch.+# location to write generated headers +sourcedir = $(top_srcdir)/src/backend/utilsCalling the output the source is bound to confuse people. The comment
implies all generated headers, not just the ones introduced by the
patch. I would just output to the current directory (i.e. have an
--output option with a default empty string). Also, if we want to
output somewhere else, I would imagine it'd be under the top builddir,
not srcdir.+$(PERL) -I $(top_srcdir)/src/include/utils $< --headerdir=$(headerdir) --sourcedir=$(sourcedir) --inputfile=$(headerdir)/commandtag.dat1. headerdir is entirely unused by the script
2. We can default to working dir for the output as mentioned above
3. -I $(top_srcdir)/src/include/utils is supposed to point to the dir
containing DataFile.pm, but since gencommandtag.pl has "use lib..."
it's probably not needed here. I don't recall why we keep the "-I"
elsewhere. (ditto in Solution.pm)I'm thinking it would look something like this:
+$(PERL) $< --inputfile=$(top_srcdir)/src/include/utils/commandtag.dat\
I have taken all this advice in v5 of the patch. --inputfile and --outputdir (previously named --sourcedir) are now optional with the defaults as you suggested.
--
utils/misc/Makefile+all: distprep + # Note: guc-file.c is not deleted by 'make clean', # since we want to ship it in distribution tarballs. clean: @rm -f lex.yy.c + +maintainer-clean: cleanSeems non-functional.
Yeah, I also had an unnecessary addition to .gitignore in that directory. I had originally placed the commandtag stuff here before moving it one directory up. Thanks for catching that.
--
DataFiles.pmI haven't studied this in detail, but I'll suggest that if this meant
to have wider application, maybe it should live in src/tools ?
We don’t seem to have a standard place for perl modules. src/test/perl has some that are specifically for tap testing, and src/backend/catalog has some for catalog data file processing. I put DataFiles.pm in src/backend/catalog because that’s where most data file processing currently is located. src/tools has PerfectHash.pm, and a bunch of Windows specific modules under src/tools/msvc.
I'm not familiar with using different IO routines depending on the OS
-- what's the benefit of that?
I think you are talking about the slurp_file routine. That came directly from the TestLib.pm module. I don't have enough perl-on-windows experience to comment about why it does things that way. I was reluctant to have DataFile.pm 'use TestLib', since DataFile has absolutely nothing to do with regression testing. I don't like copying the function, either, though I chose that as the lesser evil. Which is more evil is debateable.
src/test/perl/ contains SimpleTee.pm and RecursiveCopy.pm, neither of which contain functionality limited to just testing. I think they could be moved to src/tools. src/test/perl/TestLib.pm contains a mixture of testing specific functions and more general purpose functions. For instance, TestLib.pm contains functions to read in a file or directory (slurp_file(filepath) and slurp_dir(dirpath), respectively). I think we should have just one implementation of those in just one place. Neither TestLib nor DataFile seem appropriate, nor does src/test/perl seem right. I checked whether Perl ships with core module support for this and didn't find anything. There is a cpan module named File::Slurp, but it is not a core module so far as I can tell, and it does more than we want.
Should I submit a separate patch refactoring the location of perl modules and functions of general interest into src/tools? src/tools/perl?
I am not changing DataFile.pm's duplicate copy of slurp_file in v5 of the patch, as I don't yet know the best way to approach the problem. I expect there will have to be a v6 once this has been adequately debated.
--
gencommandtag.plslurp_without_comments() is unused.
Right. An earlier version of gencommandtag.pl didn't use DataFile.pm, and I neglected to remove this function when I transitioned to using DataFile.pm. Thanks for noticing!
sanity_check_data() seems longer than the main body of the script
(minus header boilerplate), and I wonder if we can pare it down some.
For one, I have difficulty imagining anyone would accidentally type an
unprintable or non-ascii character in a command tag and somehow not
realize it.
I'm uncertain about that. There is logic in EndCommand in tcop/dest.c that specifically warns that no encoding conversion will be performed due to the assumption that command tags contain only 7-bit ascii. I think that's a perfectly reasonable assumption in the C-code, but it needs to be checked by gencommandtag.pl because the bugs that might ensue from inserting an accent character or whatever could be subtle enough to not be caught right away. Such mistakes only get easier as time goes by, as the tendency for editors to change your quotes into "smart quotes" and such gets more common, and potentially as the assumption that PostgreSQL has been internationalized gets more common. Hopefully, we're moving more and more towards supporting non-ascii in more and more places. It might be less obvious to a contributor some years hence that they cannot stick an accented character into a command tag. (Compare, for example, that it used to be widely accepted that you shouldn't stick spaces and hyphens into file names, but now a fair number of programmers will do that without flinching.)
As for checking for unprintable characters, the case is weaker. I'm not too motivated to remove the check, though.
For another, duplicating checks that were done earlier
seems like a maintenance headache.
Hmmm. As long as gencommandtag.pl is the only user of DataFile.pm, I'm inclined to agree that we're double-checking some things. The code comments I wrote certainly say so. But if DataFile.pm gets wider adoption, it might start to accept more varied input, and then gencommandtag.pl will need to assert its own set of validation. There is also the distinction between checking that the input data file meets the syntax requirements of the *parser* vs. making certain that the vivified perl structures meet the semantic requirements of the *code generator*. You may at this point be able to assert that meeting the first guarantees meeting the second, but that can't be expected to hold indefinitely.
It would be easier to decide these matters if we knew whether commandtag logic will ever be removed and whether DataFile will ever gain wider adoption for code generation purposes....
dataerror() is defined near the top, but other functions are defined
at the bottom.
Moved.
+# Generate all output internally before outputting anything, to avoid +# partially overwriting generated files under error conditionsMy personal preference is, having this as a design requirement
sacrifices readability for unclear gain, especially since a "chunk"
also includes things like header boilerplate. That said, the script is
also short enough that it doesn't make a huge difference either way.
Catalog.pm writes a temporary file and then moves it to the final file name at the end. DataFile buffers the output and only writes it after all the code generation has succeeded. There is no principled basis for these two modules tackling the same problem in two different ways. Perhaps that's another argument for pulling this kind of functionality out of random places and consolidating it in one or more modules in src/tools.
Speaking of boilerplate, it's better for readability to separate that
from actual code such as:typedef enum CommandTag
{
#define FIRST_COMMANDTAG COMMANDTAG_$sorted[0]->{taglabel})
Good idea. While I was doing this, I also consolidated the duplicated boilerplate into just one function. I think this function, too, should go in just one perl module somewhere. See boilerplate_header() for details.
--
tcop/dest.c+ * We no longer display LastOid, but to preserve the wire protocol, + * we write InvalidOid where the LastOid used to be written. For + * efficiency in the snprintf(), hard-code InvalidOid as zero.Hmm, is hard-coding zero going to make any difference here?
Part of the value of refactoring the commandtag logic is to make it easier to remove the whole ugly mess later. Having snprintf write the Oid into the string obfuscates the stupidity of what is really being done here. Putting the zero directly into the format string makes it clearer, to my eyes, that nothing clever is afoot.
I have removed the sentence about efficiency. Thanks for mentioning it.
Attachments:
v5-0001-Migrating-commandTag-from-string-to-enum.patchapplication/octet-stream; name=v5-0001-Migrating-commandTag-from-string-to-enum.patch; x-unix-mode=0644Download
From 19408d1d58f391dfbb13d76c3b46e553b0d4ec4d Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Thu, 6 Feb 2020 11:41:44 -0800
Subject: [PATCH v5] Migrating commandTag from string to enum.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The backend was using strings to represent command tags and doing string
comparisons in multiple places. Fixing that by creating a new
CommandTag enum and using it instead.
Replacing numerous occurrences of char *completionTag with a
QueryCompletionData struct so that the code no longer stores information
about completed queries in a cstring. Only at the last moment, in
EndCommand(), does this get converted to a string.
EventTriggerCacheItem no longer holds an array of palloc’d tag strings
in sorted order, but rather just a Bitmapset over the CommandTags.
---
.../pg_stat_statements/pg_stat_statements.c | 18 +-
contrib/sepgsql/hooks.c | 6 +-
src/backend/catalog/DataFile.pm | 400 ++++++++
src/backend/commands/createas.c | 14 +-
src/backend/commands/event_trigger.c | 160 +--
src/backend/commands/matview.c | 2 +-
src/backend/commands/portalcmds.c | 16 +-
src/backend/commands/prepare.c | 4 +-
src/backend/executor/execMain.c | 4 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/spi.c | 22 +-
src/backend/replication/walsender.c | 18 +-
src/backend/tcop/dest.c | 38 +-
src/backend/tcop/postgres.c | 24 +-
src/backend/tcop/pquery.c | 111 +--
src/backend/tcop/utility.c | 558 +++++------
src/backend/utils/.gitignore | 3 +
src/backend/utils/Makefile | 20 +-
src/backend/utils/cache/evtcache.c | 27 +-
src/backend/utils/cache/plancache.c | 4 +-
src/backend/utils/commandtag.c | 119 +++
src/backend/utils/gencommandtag.pl | 266 +++++
src/backend/utils/mmgr/portalmem.c | 6 +-
src/include/Makefile | 4 +-
src/include/commands/createas.h | 3 +-
src/include/commands/event_trigger.h | 2 +-
src/include/commands/matview.h | 2 +-
src/include/commands/portalcmds.h | 3 +-
src/include/commands/prepare.h | 2 +-
src/include/nodes/parsenodes.h | 1 +
src/include/tcop/dest.h | 7 +-
src/include/tcop/pquery.h | 2 +-
src/include/tcop/utility.h | 15 +-
src/include/utils/.gitignore | 2 +
src/include/utils/commandtag.dat | 938 ++++++++++++++++++
src/include/utils/commandtag.h | 60 ++
src/include/utils/evtcache.h | 3 +-
src/include/utils/plancache.h | 7 +-
src/include/utils/portal.h | 6 +-
src/pl/plpgsql/src/pl_exec.c | 10 +-
.../test_ddl_deparse/test_ddl_deparse.c | 2 +-
src/tools/msvc/Solution.pm | 12 +
42 files changed, 2311 insertions(+), 614 deletions(-)
create mode 100644 src/backend/catalog/DataFile.pm
create mode 100644 src/backend/utils/commandtag.c
create mode 100755 src/backend/utils/gencommandtag.pl
create mode 100644 src/include/utils/commandtag.dat
create mode 100644 src/include/utils/commandtag.h
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index e0dbeebde3..1f3e9c1041 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -307,7 +307,7 @@ static void pgss_ExecutorEnd(QueryDesc *queryDesc);
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
static uint64 pgss_hash_string(const char *str, int len);
static void pgss_store(const char *query, uint64 queryId,
int query_location, int query_len,
@@ -960,7 +960,7 @@ static void
pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context,
ParamListInfo params, QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
@@ -998,11 +998,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
@@ -1013,10 +1013,8 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
INSTR_TIME_SET_CURRENT(duration);
INSTR_TIME_SUBTRACT(duration, start);
- /* parse command tag to retrieve the number of affected rows. */
- if (completionTag &&
- strncmp(completionTag, "COPY ", 5) == 0)
- rows = pg_strtouint64(completionTag + 5, NULL, 10);
+ if (qc && qc->commandTag == COMMANDTAG_COPY)
+ rows = qc->nprocessed;
else
rows = 0;
@@ -1060,11 +1058,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
}
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 997a64c87e..853b5b04ab 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -317,7 +317,7 @@ sepgsql_utility_command(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
@@ -380,11 +380,11 @@ sepgsql_utility_command(PlannedStmt *pstmt,
if (next_ProcessUtility_hook)
(*next_ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
diff --git a/src/backend/catalog/DataFile.pm b/src/backend/catalog/DataFile.pm
new file mode 100644
index 0000000000..f7bf7656fc
--- /dev/null
+++ b/src/backend/catalog/DataFile.pm
@@ -0,0 +1,400 @@
+#!/usr/bin/perl -w
+#----------------------------------------------------------------------
+# DataFile.pm
+# Perl module that safely converts data files into perl data.
+#
+# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/backend/catalog/DataFile.pm
+#
+#----------------------------------------------------------------------
+
+
+=pod
+
+=head1 NAME
+
+DataFile - module for safely parsing and vivifying perl data files
+
+=head1 SYNOPSIS
+
+ use DataFile;
+
+ my $array_reference = DataFile::ParseData("myfile.dat");
+
+=head1 DESCRIPTION
+
+DataFile contains a parsing function, ParseData, for parsing various
+data files. By convention, these data files are stored as a single array
+of hashes of key/value pairs.
+
+=pod
+
+=head1 METHODS
+
+=cut
+
+package DataFile;
+
+use strict;
+use warnings;
+use Config;
+
+=pod
+
+=over
+
+=item slurp_file(filename)
+
+Return the full contents of the specified file.
+
+=cut
+
+sub slurp_file
+{
+ my ($filename) = @_;
+ local $/;
+ my $contents;
+ if ($Config{osname} ne 'MSWin32')
+ {
+ open(my $in, '<', $filename)
+ or die "could not read \"$filename\": $!";
+ $contents = <$in>;
+ close $in;
+ }
+ else
+ {
+ my $fHandle = createFile($filename, "r", "rwd")
+ or die "could not open \"$filename\": $^E";
+ OsFHandleOpen(my $fh = IO::Handle->new(), $fHandle, 'r')
+ or die "could not read \"$filename\": $^E\n";
+ $contents = <$fh>;
+ CloseHandle($fHandle)
+ or die "could not close \"$filename\": $^E\n";
+ }
+ $contents =~ s/\r//g if $Config{osname} eq 'msys';
+ return $contents;
+}
+
+=pod
+
+=item ParseData(filename)
+
+Parse a file which represents a valid perl array reference of hash references,
+vivify the perl array reference, and return it.
+
+This function is paranoid about evaluation or interpolation of embedded perl
+and will die with an error message if the file contains anything that looks
+like it will result in code being executed.
+
+Mistakes in the data file making it an invalid perl object will draw errors
+about the line number in the file where parsing failed and an attempt to
+indicate what parsing problem was encountered there.
+
+=cut
+
+our (%looking, $pos);
+sub update_search($$)
+{
+ my ($lookingfor, $atpos) = @_;
+ if ($atpos > $pos)
+ {
+ %looking = ($lookingfor => 1);
+ $pos = $atpos;
+ }
+ elsif ($atpos == $pos)
+ {
+ $looking{$lookingfor} = 1;
+ }
+}
+
+sub ParseData
+{
+ my ($inputfile) = @_;
+
+ # Vivify the input data, but be paranoid about eval'ing arbitrary code
+ my $data;
+ eval {
+ my $untrusted = slurp_file($inputfile);
+ our $noeval = undef;
+ our $noevalstr = undef;
+ our $noevalpos = -1;
+ %looking = ();
+ $pos = -1;
+
+ # A quotable string is one that does not embed an unescaped end-quote
+ # or end with a backslash which would escape the quote that otherwise
+ # terminates the quoted string.
+ my $singlequotablere = qr/
+ # Verify through look-behind that we're at the
+ # beginning of a single-quoted string
+ (?<=')
+ # Consume the single-quoted string contents
+ # without backtracking or givebacks.
+ (?>
+ # Any single character except single-quote
+ # or backslash
+ [^'\\]
+ |
+ # Any escaped character, including escaped
+ # single-quotes and escaped backslashes
+ \\.
+ )*+
+ # Verify through look-ahead that we're at the
+ # end of a single-quoted string
+ (?=')
+ /msx;
+
+ # Inside double-quoted strings, Perl interpolates substrings which look
+ # like variables with those variables' values. This seems a bit unsafe
+ # to allow in this package, so we check double-quoted strings for
+ # anything that Perl might interpret this way at 'eval' time. Such
+ # substrings still match our pattern, but we set a flag alerting us to
+ # not proceed to interpolate the substring.
+ my $doublequotablere = qr/
+ # Verify through look-behind that we're at the
+ # beginning of a double-quoted string
+ (?<=")
+ # Begin capturing. If we encounter an illegal
+ # sequence, we can save the captured string for
+ # use later during error reporting
+ (
+ # Consume the double-quoted string contents
+ # without backtracking or givebacks.
+ (?>
+ # Any character except double-quote,
+ # backslash, or variable indicators [$%@]
+ [^"\\%@\$]
+ |
+ # Any escaped character
+ \\.
+ |
+ # A variable indicator, but we set the flag
+ # that we must not eval this string
+ [%@\$](?{ local $noeval = 1; })
+ )*+
+ # verify through look-ahead that we're at the
+ # end of the double-quoted string
+ (?=")
+ # End capture
+ )
+ # If we encountered a disallowed character
+ # during the capture, record the entire captured
+ # string for later
+ (?{ $noevalstr = $^N, $noevalpos = pos()
+ if ($noeval && !defined $noevalstr);
+ })
+ /msx;
+
+ my $quotedstringre = qr/
+ (?:
+ '
+ (?{ update_search("end of single quoted string", pos()) })
+ $singlequotablere
+ '
+ |
+ "
+ (?{ update_search("end of double quoted string", pos()) })
+ $doublequotablere
+ "
+ )
+ /msx;
+
+ # A perl comment cannot occur inside a quoted string, but otherwise it can
+ # occur pretty much anywhere. So long as we're not inside a quotation, we
+ # can match a # to the end of the line
+ my $commentre = qr/
+ (?:
+ (?{ update_search("comment", pos()) })
+ # Beginning of a comment
+ \#
+ # Anything other than end-of-line, no backtracking,
+ # and no give-backs
+ (?>[^\r\n])*+
+ # end-of-line
+ \r?\n
+ )
+ /msx;
+
+ # Comments and/or whitespace can occur just about anywhere. It is convenient
+ # to have a single regex that matches them so we don't have to think about
+ # comments much from here onward.
+ my $spacere = qr/
+ (?:
+ # any number of comment lines, including none, including
+ # leading whitespace before the comment begins
+ (?:
+ (?{ update_search("whitespace", pos()) })
+ (?>\s)*+ # whitespace without backtracking
+ (?{ update_search("comment", pos()) })
+ $commentre
+ )*+
+ # whitespace following the zero or more comment lines,
+ # without backtracking
+ (?{ update_search("whitespace", pos()) })
+ (?>\s)*+
+ )
+ /msx;
+
+ # Keys must be either quoted strings, numbers, or barewords if followed
+ # by an arrow operator. The arrow operator is not part of the key, however,
+ # so we check that with a look-ahead assertion
+ my $keyre = qr/
+ (?:
+ $quotedstringre
+ |
+ \d+
+ |
+ # bareword only allowed before arrow op, but there cannot
+ # be comments inbetween, only whitespace
+ \w+(?=\s*=>)
+ )
+ /msx;
+ # Values must be either numbers or quoted strings
+ my $valuere = qr/
+ (?:
+ $quotedstringre
+ |
+ \d+
+ )
+ /msx;
+
+ # Key/Value pairs can use arrow or comma separators. We hid the complexity
+ # of arrow operator quoting in the keyre, above
+ my $keyvaluere = qr/
+ (?{ update_search("key value pair", pos()) })
+ (?:
+ $keyre
+ $spacere
+ (?:
+ =>
+ |
+ ,
+ )
+ $spacere
+ $valuere
+ )
+ /msx;
+
+ # Key/Value pair lists
+ my $keyvaluelistre = qr/
+ # Optional key-value pair
+ (?:
+ $keyvaluere
+ $spacere
+
+ # Optionally followed by more
+ (?:
+ , # Required
+ $spacere
+ $keyvaluere
+ )*
+ # Optionally ending with a comma
+ (?:
+ $spacere
+ ,
+ )?
+ )?
+ /msx;
+
+ # Hash reference comprised of key/value pairs
+ my $hashrefre = qr/
+ (?:
+ (?{ update_search("beginning of hash reference", pos()) })
+ \{ # Open hash
+ $spacere
+ $keyvaluelistre
+ $spacere
+ (?{ update_search("ending of hash reference", pos()) })
+ \} # Close hash
+ )
+ /msx;
+
+ # List of hash references
+ my $hashreflist = qr/
+ # Optional hash ref
+ (?:
+ $hashrefre
+ $spacere
+
+ # Optionally followed by more
+ (?:
+ , # Required
+ $spacere
+ $hashrefre
+ )*+
+ # Optionally ending with a comma
+ (?:
+ $spacere
+ ,
+ )?+
+ )?+
+ /msx;
+
+ # Reference to list of hash references
+ my $aryofhashrefre = qr/
+ (?:
+ (?{ update_search("beginning of array reference", pos()) })
+ \[ # Open array
+ $spacere
+ $hashreflist
+ $spacere
+ (?{ update_search("ending of array reference", pos()) })
+ \] # Close array
+ )
+ /msx;
+
+
+ # After removing all comments, remaining structure must strictly
+ # match an array reference of hash references of key/value pairs
+ # with no code that perl's eval() could be tricked into running.
+ #
+ # This parser is stricter than necessary, but it should allow all
+ # data to be specified that we need it to allow, so instead of
+ # making this parser more complicated (or permissive), see if you
+ # can make the data file follow the expected format.
+ #
+ if ($untrusted =~ m/^
+ $spacere
+ $aryofhashrefre
+ $spacere
+ $/msx)
+ {
+ # If we encountered anything that might cause eval() to perform
+ # variable interpolation, complain and do not eval(). This may
+ # be overly paranoid....
+ if (defined $noevalstr)
+ {
+ my $lineno = scalar(split/\n/, substr($untrusted, 0, $noevalpos));
+ die "\nPossible attempt at variable interpolation in double " .
+ "quoted string in $inputfile, line $lineno: \"$noevalstr\"";
+ }
+
+ # We're treating the input file as a piece of Perl, so we
+ # need to use string eval here. Tell perlcritic we know what
+ # we're doing.
+ #
+ $data = eval $untrusted; ## no critic (ProhibitStringyEval)
+ die $@ if $@;
+ }
+ else
+ {
+ my $lineno = scalar(split/\n/, substr($untrusted, 0, $pos));
+ my $looking = join(" or ", keys %looking);
+ die("\n$inputfile does not match our expected format;\n" .
+ "At line $lineno, looking for $looking\n");
+ }
+ };
+ die $@ if $@;
+
+ return $data;
+}
+
+=pod
+
+=back
+
+=cut
+
+1;
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cc02cf824e..3bcd7a95af 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -10,7 +10,7 @@
*
* Formerly, CTAS was implemented as a variant of SELECT, which led
* to assorted legacy behaviors that we still try to preserve, notably that
- * we must return a tuples-processed count in the completionTag. (We no
+ * we must return a tuples-processed count in the QueryCompletion. (We no
* longer do that for CTAS ... WITH NO DATA, however.)
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
@@ -225,7 +225,7 @@ create_ctas_nodata(List *tlist, IntoClause *into)
ObjectAddress
ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ParamListInfo params, QueryEnvironment *queryEnv,
- char *completionTag)
+ QueryCompletion *qc)
{
Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
@@ -270,7 +270,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
Assert(!is_matview); /* excluded by syntax */
- ExecuteQuery(pstate, estmt, into, params, dest, completionTag);
+ ExecuteQuery(pstate, estmt, into, params, dest, qc);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
@@ -352,11 +352,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* run the plan to completion */
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
- /* save the rowcount if we're given a completionTag to fill */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ /* save the rowcount if we're given a qc to fill */
+ if (qc)
+ SetQueryCompletion(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 71911d4067..7c8415475f 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -78,59 +78,6 @@ typedef struct
bool supported;
} event_trigger_support_data;
-typedef enum
-{
- EVENT_TRIGGER_COMMAND_TAG_OK,
- EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED,
- EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED
-} event_trigger_command_tag_check_result;
-
-/* XXX merge this with ObjectTypeMap? */
-static const event_trigger_support_data event_trigger_support[] = {
- {"ACCESS METHOD", true},
- {"AGGREGATE", true},
- {"CAST", true},
- {"CONSTRAINT", true},
- {"COLLATION", true},
- {"CONVERSION", true},
- {"DATABASE", false},
- {"DOMAIN", true},
- {"EXTENSION", true},
- {"EVENT TRIGGER", false},
- {"FOREIGN DATA WRAPPER", true},
- {"FOREIGN TABLE", true},
- {"FUNCTION", true},
- {"INDEX", true},
- {"LANGUAGE", true},
- {"MATERIALIZED VIEW", true},
- {"OPERATOR", true},
- {"OPERATOR CLASS", true},
- {"OPERATOR FAMILY", true},
- {"POLICY", true},
- {"PROCEDURE", true},
- {"PUBLICATION", true},
- {"ROLE", false},
- {"ROUTINE", true},
- {"RULE", true},
- {"SCHEMA", true},
- {"SEQUENCE", true},
- {"SERVER", true},
- {"STATISTICS", true},
- {"SUBSCRIPTION", true},
- {"TABLE", true},
- {"TABLESPACE", false},
- {"TRANSFORM", true},
- {"TRIGGER", true},
- {"TEXT SEARCH CONFIGURATION", true},
- {"TEXT SEARCH DICTIONARY", true},
- {"TEXT SEARCH PARSER", true},
- {"TEXT SEARCH TEMPLATE", true},
- {"TYPE", true},
- {"USER MAPPING", true},
- {"VIEW", true},
- {NULL, false}
-};
-
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -150,8 +97,6 @@ typedef struct SQLDropObject
static void AlterEventTriggerOwner_internal(Relation rel,
HeapTuple tup,
Oid newOwnerId);
-static event_trigger_command_tag_check_result check_ddl_tag(const char *tag);
-static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(const char *tag);
static void error_duplicate_filter_variable(const char *defname);
static Datum filter_list_to_array(List *filterlist);
static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname,
@@ -259,71 +204,23 @@ validate_ddl_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
- event_trigger_command_tag_check_result result;
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
- result = check_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED)
+ if (commandTag == COMMANDTAG_UNKNOWN)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
- tag, filtervar)));
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
+ tagstr, filtervar)));
+ if ( ! command_tag_event_trigger_ok(commandTag))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
-static event_trigger_command_tag_check_result
-check_ddl_tag(const char *tag)
-{
- const char *obtypename;
- const event_trigger_support_data *etsd;
-
- /*
- * Handle some idiosyncratic special cases.
- */
- if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
- pg_strcasecmp(tag, "SELECT INTO") == 0 ||
- pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
- pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
- pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
- pg_strcasecmp(tag, "COMMENT") == 0 ||
- pg_strcasecmp(tag, "GRANT") == 0 ||
- pg_strcasecmp(tag, "REVOKE") == 0 ||
- pg_strcasecmp(tag, "DROP OWNED") == 0 ||
- pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
- /*
- * Otherwise, command should be CREATE, ALTER, or DROP.
- */
- if (pg_strncasecmp(tag, "CREATE ", 7) == 0)
- obtypename = tag + 7;
- else if (pg_strncasecmp(tag, "ALTER ", 6) == 0)
- obtypename = tag + 6;
- else if (pg_strncasecmp(tag, "DROP ", 5) == 0)
- obtypename = tag + 5;
- else
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
-
- /*
- * ...and the object type should be something recognizable.
- */
- for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++)
- if (pg_strcasecmp(etsd->obtypename, obtypename) == 0)
- break;
- if (etsd->obtypename == NULL)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
- if (!etsd->supported)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-}
-
/*
* Validate DDL command tags for event table_rewrite.
*/
@@ -334,29 +231,18 @@ validate_table_rewrite_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
- event_trigger_command_tag_check_result result;
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
- result = check_table_rewrite_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
+ if (! command_tag_table_rewrite_ok(commandTag))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
-static event_trigger_command_tag_check_result
-check_table_rewrite_ddl_tag(const char *tag)
-{
- if (pg_strcasecmp(tag, "ALTER TABLE") == 0 ||
- pg_strcasecmp(tag, "ALTER TYPE") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
-}
-
/*
* Complain about a duplicate filter variable.
*/
@@ -663,7 +549,7 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
* tags matching.
*/
static bool
-filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
+filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
{
/*
* Filter by session replication role, knowing that we never see disabled
@@ -681,9 +567,7 @@ filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
}
/* Filter by tags, if any were specified. */
- if (item->ntags != 0 && bsearch(tag, item->tag,
- item->ntags, sizeof(char *),
- pg_qsort_strcmp) == NULL)
+ if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
return false;
/* if we reach that point, we're not filtering out this item */
@@ -700,7 +584,7 @@ EventTriggerCommonSetup(Node *parsetree,
EventTriggerEvent event, const char *eventstr,
EventTriggerData *trigdata)
{
- const char *tag;
+ CommandTag tag;
List *cachelist;
ListCell *lc;
List *runlist = NIL;
@@ -716,25 +600,25 @@ EventTriggerCommonSetup(Node *parsetree,
*
* If this cross-check fails for you, you probably need to either adjust
* standard_ProcessUtility() not to invoke event triggers for the command
- * type in question, or you need to adjust check_ddl_tag to accept the
+ * type in question, or you need to adjust event_trigger_ok to accept the
* relevant command tag.
*/
#ifdef USE_ASSERT_CHECKING
{
- const char *dbgtag;
+ CommandTag dbgtag;
dbgtag = CreateCommandTag(parsetree);
if (event == EVT_DDLCommandStart ||
event == EVT_DDLCommandEnd ||
event == EVT_SQLDrop)
{
- if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ if (! command_tag_event_trigger_ok(dbgtag))
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
else if (event == EVT_TableRewrite)
{
- if (check_table_rewrite_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ if (! command_tag_table_rewrite_ok(dbgtag))
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
}
#endif
@@ -758,7 +642,7 @@ EventTriggerCommonSetup(Node *parsetree,
{
EventTriggerCacheItem *item = lfirst(lc);
- if (filter_event_trigger(&tag, item))
+ if (filter_event_trigger(tag, item))
{
/* We must plan to fire this trigger. */
runlist = lappend_oid(runlist, item->fnoid);
@@ -2136,7 +2020,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
values[i++] = Int32GetDatum(addr.objectSubId);
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
/* object_type */
values[i++] = CStringGetTextDatum(type);
/* schema */
@@ -2161,7 +2045,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
nulls[i++] = true;
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
/* object_type */
values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
/* schema */
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 1ee37c1aeb..c3954f3e24 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -136,7 +136,7 @@ SetMatViewPopulatedState(Relation relation, bool newstate)
*/
ObjectAddress
ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag)
+ ParamListInfo params, QueryCompletion *qc)
{
Oid matviewOid;
Relation matviewRel;
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 7e5c805a1e..744d79a2e2 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -106,7 +106,8 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
PortalDefineQuery(portal,
NULL,
queryString,
- "SELECT", /* cursor's query is always a SELECT */
+ COMMANDTAG_SELECT, /* cursor's query is always a
+ * SELECT */
list_make1(plan),
NULL);
@@ -160,15 +161,14 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
*
* stmt: parsetree node for command
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store a command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*/
void
PerformPortalFetch(FetchStmt *stmt,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Portal portal;
uint64 nprocessed;
@@ -203,10 +203,8 @@ PerformPortalFetch(FetchStmt *stmt,
dest);
/* Return command status if wanted */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT,
- stmt->ismove ? "MOVE" : "FETCH",
- nprocessed);
+ if (qc)
+ SetQueryCompletion(qc, stmt->ismove ? COMMANDTAG_MOVE : COMMANDTAG_FETCH, nprocessed);
}
/*
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index c4e4b6eaec..f917fc9c7a 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -187,7 +187,7 @@ void
ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
PreparedStatement *entry;
CachedPlan *cplan;
@@ -288,7 +288,7 @@ ExecuteQuery(ParseState *pstate,
*/
PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
- (void) PortalRun(portal, count, false, true, dest, dest, completionTag);
+ (void) PortalRun(portal, count, false, true, dest, dest, qc);
PortalDrop(portal, false);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index ee5c3a60ff..751b36b6bf 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -787,11 +787,11 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
if (isTempNamespace(get_rel_namespace(rte->relid)))
continue;
- PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
+ PreventCommandTagIfReadOnly(CreateCommandTag((Node *) plannedstmt));
}
if (plannedstmt->commandType != CMD_SELECT || plannedstmt->hasModifyingCTE)
- PreventCommandIfParallelMode(CreateCommandTag((Node *) plannedstmt));
+ PreventCommandTagIfParallelMode(CreateCommandTag((Node *) plannedstmt));
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 5cff6c4321..9b45a8a9a0 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -530,7 +530,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a SQL function",
- CreateCommandTag(stmt->utilityStmt))));
+ CreateCommandName(stmt->utilityStmt))));
}
if (fcache->readonly_func && !CommandIsReadOnly(stmt))
@@ -538,7 +538,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ CreateCommandName((Node *) stmt))));
/* OK, build the execution_state for this query */
newes = (execution_state *) palloc(sizeof(execution_state));
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index c46764bf42..d0a73778ed 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1338,7 +1338,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
/* translator: %s is name of a SQL command, eg INSERT */
errmsg("cannot open %s query as cursor",
- plansource->commandTag)));
+ GetCommandTagName(plansource->commandTag))));
}
Assert(list_length(plan->plancache_list) == 1);
@@ -1469,7 +1469,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) pstmt))));
+ CreateCommandName((Node *) pstmt))));
}
}
@@ -2255,7 +2255,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ CreateCommandName((Node *) stmt))));
/*
* If not read-only mode, advance the command counter before each
@@ -2291,9 +2291,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else
{
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
ProcessUtilityContext context;
+ InitializeQueryCompletion(&qc);
+
/*
* If the SPI context is atomic, or we are asked to manage
* snapshots, then we are in an atomic execution context.
@@ -2312,7 +2314,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
paramLI,
_SPI_current->queryEnv,
dest,
- completionTag);
+ &qc);
/* Update "processed" if stmt returned tuples */
if (_SPI_current->tuptable)
@@ -2328,9 +2330,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
{
CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
- if (strncmp(completionTag, "SELECT ", 7) == 0)
- _SPI_current->processed =
- pg_strtouint64(completionTag + 7, NULL, 10);
+ if (qc.commandTag == COMMANDTAG_SELECT)
+ _SPI_current->processed = qc.nprocessed;
else
{
/*
@@ -2351,9 +2352,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else if (IsA(stmt->utilityStmt, CopyStmt))
{
- Assert(strncmp(completionTag, "COPY ", 5) == 0);
- _SPI_current->processed = pg_strtouint64(completionTag + 5,
- NULL, 10);
+ Assert(qc.commandTag == COMMANDTAG_COPY);
+ _SPI_current->processed = qc.nprocessed;
}
}
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index abb533b9d0..dc6c16101b 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1074,8 +1074,11 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
static void
DropReplicationSlot(DropReplicationSlotCmd *cmd)
{
+ QueryCompletion qc;
+
ReplicationSlotDrop(cmd->slotname, !cmd->wait);
- EndCommand("DROP_REPLICATION_SLOT", DestRemote);
+ SetQueryCompletion(&qc, COMMANDTAG_DROP_REPLICATION_SLOT, 0);
+ EndCommand(&qc, DestRemote, false);
}
/*
@@ -1086,6 +1089,7 @@ static void
StartLogicalReplication(StartReplicationCmd *cmd)
{
StringInfoData buf;
+ QueryCompletion qc;
/* make sure that our requirements are still fulfilled */
CheckLogicalDecodingRequirements();
@@ -1160,7 +1164,8 @@ StartLogicalReplication(StartReplicationCmd *cmd)
WalSndSetState(WALSNDSTATE_STARTUP);
/* Get out of COPY mode (CommandComplete). */
- EndCommand("COPY 0", DestRemote);
+ SetQueryCompletion(&qc, COMMANDTAG_COPY, 0);
+ EndCommand(&qc, DestRemote, false);
}
/*
@@ -1464,6 +1469,7 @@ exec_replication_command(const char *cmd_string)
Node *cmd_node;
MemoryContext cmd_context;
MemoryContext old_context;
+ QueryCompletion qc;
/*
* If WAL sender has been told that shutdown is getting close, switch its
@@ -1614,7 +1620,8 @@ exec_replication_command(const char *cmd_string)
MemoryContextDelete(cmd_context);
/* Send CommandComplete message */
- EndCommand("SELECT", DestRemote);
+ SetQueryCompletion(&qc, COMMANDTAG_SELECT, 0);
+ EndCommand(&qc, DestRemote, true);
/* Report to pgstat that this process is now idle */
pgstat_report_activity(STATE_IDLE, NULL);
@@ -2867,8 +2874,11 @@ WalSndDone(WalSndSendDataCallback send_data)
if (WalSndCaughtUp && sentPtr == replicatedPtr &&
!pq_is_send_pending())
{
+ QueryCompletion qc;
+
/* Inform the standby that XLOG streaming is done */
- EndCommand("COPY 0", DestRemote);
+ SetQueryCompletion(&qc, COMMANDTAG_COPY, 0);
+ EndCommand(&qc, DestRemote, false);
pq_flush();
proc_exit(0);
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 09c1dcbb53..5e6ab93f49 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -100,7 +100,7 @@ DestReceiver *None_Receiver = (DestReceiver *) &donothingDR;
* ----------------
*/
void
-BeginCommand(const char *commandTag, CommandDest dest)
+BeginCommand(CommandTag commandTag, CommandDest dest)
{
/* Nothing to do at present */
}
@@ -163,8 +163,12 @@ CreateDestReceiver(CommandDest dest)
* ----------------
*/
void
-EndCommand(const char *commandTag, CommandDest dest)
+EndCommand(const QueryCompletion *qc, CommandDest dest, bool force_undecorated_output)
{
+ char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag tag;
+ const char *tagname;
+
switch (dest)
{
case DestRemote:
@@ -172,11 +176,33 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestRemoteSimple:
/*
- * We assume the commandTag is plain ASCII and therefore requires
- * no encoding conversion.
+ * We assume the tagname is plain ASCII and therefore requires no
+ * encoding conversion.
+ *
+ * We no longer display LastOid, but to preserve the wire protocol,
+ * we write InvalidOid where the LastOid used to be written.
+ *
+ * All cases where LastOid was written also write nprocessed count,
+ * so just Assert that rather than having an extra test.
*/
- pq_putmessage('C', commandTag, strlen(commandTag) + 1);
- break;
+ tag = qc->commandTag;
+ tagname = GetCommandTagName(tag);
+
+ if (command_tag_display_last_oid(tag) && !force_undecorated_output)
+ {
+ /* We assume InvalidOid is zero in the snprintf format string */
+ Assert(InvalidOid == 0);
+ /* We assume rowcount will always be wanted when last_oid is wanted */
+ Assert(command_tag_display_rowcount(tag));
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "%s 0 " UINT64_FORMAT, tagname, qc->nprocessed);
+ }
+ else if (command_tag_display_rowcount(tag) && !force_undecorated_output)
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "%s " UINT64_FORMAT, tagname, qc->nprocessed);
+ else
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s", tagname);
+ pq_putmessage('C', completionTag, strlen(completionTag) + 1);
case DestNone:
case DestDebug:
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0a6f80963b..62c181b0a5 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1064,8 +1064,8 @@ exec_simple_query(const char *query_string)
{
RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);
bool snapshot_set = false;
- const char *commandTag;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag commandTag;
+ QueryCompletion qc;
MemoryContext per_parsetree_context = NULL;
List *querytree_list,
*plantree_list;
@@ -1081,7 +1081,7 @@ exec_simple_query(const char *query_string)
*/
commandTag = CreateCommandTag(parsetree->stmt);
- set_ps_display(commandTag, false);
+ set_ps_display(GetCommandTagName(commandTag), false);
BeginCommand(commandTag, dest);
@@ -1230,7 +1230,7 @@ exec_simple_query(const char *query_string)
true,
receiver,
receiver,
- completionTag);
+ &qc);
receiver->rDestroy(receiver);
@@ -1281,7 +1281,7 @@ exec_simple_query(const char *query_string)
* command the client sent, regardless of rewriting. (But a command
* aborted by error will not send an EndCommand report at all.)
*/
- EndCommand(completionTag, dest);
+ EndCommand(&qc, dest, false);
/* Now we may drop the per-parsetree context, if one was created. */
if (per_parsetree_context)
@@ -1343,7 +1343,7 @@ exec_parse_message(const char *query_string, /* string to execute */
MemoryContext oldcontext;
List *parsetree_list;
RawStmt *raw_parse_tree;
- const char *commandTag;
+ CommandTag commandTag;
List *querytree_list;
CachedPlanSource *psrc;
bool is_named;
@@ -1505,7 +1505,7 @@ exec_parse_message(const char *query_string, /* string to execute */
{
/* Empty input string. This is legal. */
raw_parse_tree = NULL;
- commandTag = NULL;
+ commandTag = COMMANDTAG_UNKNOWN;
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
querytree_list = NIL;
}
@@ -2022,7 +2022,7 @@ exec_execute_message(const char *portal_name, long max_rows)
DestReceiver *receiver;
Portal portal;
bool completed;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
const char *sourceText;
const char *prepStmtName;
ParamListInfo portalParams;
@@ -2049,7 +2049,7 @@ exec_execute_message(const char *portal_name, long max_rows)
* If the original query was a null string, just return
* EmptyQueryResponse.
*/
- if (portal->commandTag == NULL)
+ if (portal->commandTag == COMMANDTAG_UNKNOWN)
{
Assert(portal->stmts == NIL);
NullCommand(dest);
@@ -2095,7 +2095,7 @@ exec_execute_message(const char *portal_name, long max_rows)
pgstat_report_activity(STATE_RUNNING, sourceText);
- set_ps_display(portal->commandTag, false);
+ set_ps_display(GetCommandTagName(portal->commandTag), false);
if (save_log_statement_stats)
ResetUsage();
@@ -2176,7 +2176,7 @@ exec_execute_message(const char *portal_name, long max_rows)
!execute_is_fetch && max_rows == FETCH_ALL,
receiver,
receiver,
- completionTag);
+ &qc);
receiver->rDestroy(receiver);
@@ -2209,7 +2209,7 @@ exec_execute_message(const char *portal_name, long max_rows)
}
/* Send appropriate CommandComplete to client */
- EndCommand(completionTag, dest);
+ EndCommand(&qc, dest, false);
}
else
{
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 0f5801e046..f2089f5c34 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -40,7 +40,7 @@ static void ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletion *qc);
static void FillPortalStore(Portal portal, bool isTopLevel);
static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
DestReceiver *dest);
@@ -48,11 +48,11 @@ static uint64 PortalRunSelect(Portal portal, bool forward, long count,
DestReceiver *dest);
static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
static void PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletion *qc);
static uint64 DoPortalRunFetch(Portal portal,
FetchDirection fdirection,
long count,
@@ -125,10 +125,9 @@ FreeQueryDesc(QueryDesc *qdesc)
* sourceText: the source text of the query
* params: any parameters needed
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store the command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want a status string.
*
* Must be called in a memory context that will be reset or deleted on
* error; otherwise the executor's memory usage will be leaked.
@@ -139,7 +138,7 @@ ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
QueryDesc *queryDesc;
@@ -161,38 +160,26 @@ ProcessQuery(PlannedStmt *plan,
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
/*
- * Build command completion status string, if caller wants one.
+ * Build command completion status data, if caller wants one.
*/
- if (completionTag)
+ if (qc)
{
- Oid lastOid;
-
switch (queryDesc->operation)
{
case CMD_SELECT:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, COMMANDTAG_SELECT, queryDesc->estate->es_processed);
break;
case CMD_INSERT:
- /* lastoid doesn't exist anymore */
- lastOid = InvalidOid;
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "INSERT %u " UINT64_FORMAT,
- lastOid, queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, COMMANDTAG_INSERT, queryDesc->estate->es_processed);
break;
case CMD_UPDATE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "UPDATE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, COMMANDTAG_UPDATE, queryDesc->estate->es_processed);
break;
case CMD_DELETE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "DELETE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, COMMANDTAG_DELETE, queryDesc->estate->es_processed);
break;
default:
- strcpy(completionTag, "???");
+ SetQueryCompletion(qc, COMMANDTAG_UNKNOWN, queryDesc->estate->es_processed);
break;
}
}
@@ -675,9 +662,8 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
*
* altdest: where to send output of non-primary queries
*
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
- * May be NULL if caller doesn't want a status string.
+ * qc: where to store command completion status data.
+ * May be NULL if caller doesn't want status data.
*
* Returns true if the portal's execution is complete, false if it was
* suspended due to exhaustion of the count parameter.
@@ -685,7 +671,7 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
bool
PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletion *qc)
{
bool result;
uint64 nprocessed;
@@ -700,9 +686,9 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
TRACE_POSTGRESQL_QUERY_EXECUTE_START();
- /* Initialize completion tag to empty string */
- if (completionTag)
- completionTag[0] = '\0';
+ /* Initialize empty completion data */
+ if (qc)
+ InitializeQueryCompletion(qc);
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
{
@@ -771,16 +757,12 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
/*
* If the portal result contains a command tag and the caller
- * gave us a pointer to store it, copy it. Patch the "SELECT"
- * tag to also provide the rowcount.
+ * gave us a pointer to store it, copy it and update the rowcount.
*/
- if (completionTag && portal->commandTag)
+ if (qc && portal->qc.commandTag != COMMANDTAG_UNKNOWN)
{
- if (strcmp(portal->commandTag, "SELECT") == 0)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT, nprocessed);
- else
- strcpy(completionTag, portal->commandTag);
+ CopyQueryCompletion(qc, &portal->qc);
+ qc->nprocessed = nprocessed;
}
/* Mark portal not active */
@@ -794,7 +776,7 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
case PORTAL_MULTI_QUERY:
PortalRunMulti(portal, isTopLevel, false,
- dest, altdest, completionTag);
+ dest, altdest, qc);
/* Prevent portal's commands from being re-executed */
MarkPortalDone(portal);
@@ -1005,8 +987,9 @@ static void
FillPortalStore(Portal portal, bool isTopLevel)
{
DestReceiver *treceiver;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
+ InitializeQueryCompletion(&qc);
PortalCreateHoldStore(portal);
treceiver = CreateDestReceiver(DestTuplestore);
SetTuplestoreDestReceiverParams(treceiver,
@@ -1014,8 +997,6 @@ FillPortalStore(Portal portal, bool isTopLevel)
portal->holdContext,
false);
- completionTag[0] = '\0';
-
switch (portal->strategy)
{
case PORTAL_ONE_RETURNING:
@@ -1028,12 +1009,12 @@ FillPortalStore(Portal portal, bool isTopLevel)
* portal's holdSnapshot to the snapshot used (or a copy of it).
*/
PortalRunMulti(portal, isTopLevel, true,
- treceiver, None_Receiver, completionTag);
+ treceiver, None_Receiver, &qc);
break;
case PORTAL_UTIL_SELECT:
PortalRunUtility(portal, linitial_node(PlannedStmt, portal->stmts),
- isTopLevel, true, treceiver, completionTag);
+ isTopLevel, true, treceiver, &qc);
break;
default:
@@ -1042,9 +1023,9 @@ FillPortalStore(Portal portal, bool isTopLevel)
break;
}
- /* Override default completion tag with actual command result */
- if (completionTag[0] != '\0')
- portal->commandTag = pstrdup(completionTag);
+ /* Override portal completion data with actual command results */
+ if (qc.commandTag != COMMANDTAG_UNKNOWN)
+ CopyQueryCompletion(&portal->qc, &qc);
treceiver->rDestroy(treceiver);
}
@@ -1130,7 +1111,7 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count,
static void
PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
Node *utilityStmt = pstmt->utilityStmt;
Snapshot snapshot;
@@ -1178,7 +1159,7 @@ PortalRunUtility(Portal portal, PlannedStmt *pstmt,
portal->portalParams,
portal->queryEnv,
dest,
- completionTag);
+ qc);
/* Some utility statements may change context on us */
MemoryContextSwitchTo(portal->portalContext);
@@ -1202,7 +1183,7 @@ static void
PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletion *qc)
{
bool active_snapshot_set = false;
ListCell *stmtlist_item;
@@ -1284,7 +1265,7 @@ PortalRunMulti(Portal portal,
portal->sourceText,
portal->portalParams,
portal->queryEnv,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1319,7 +1300,7 @@ PortalRunMulti(Portal portal,
Assert(!active_snapshot_set);
/* statement can set tag string */
PortalRunUtility(portal, pstmt, isTopLevel, false,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1350,8 +1331,8 @@ PortalRunMulti(Portal portal,
PopActiveSnapshot();
/*
- * If a command completion tag was supplied, use it. Otherwise use the
- * portal's commandTag as the default completion tag.
+ * If a query completion data was supplied, use it. Otherwise use the
+ * portal's query completion data.
*
* Exception: Clients expect INSERT/UPDATE/DELETE tags to have counts, so
* fake them with zeros. This can happen with DO INSTEAD rules if there
@@ -1361,18 +1342,12 @@ PortalRunMulti(Portal portal,
* e.g. an INSERT that does an UPDATE instead should not print "0 1" if
* one row was updated. See QueryRewrite(), step 3, for details.
*/
- if (completionTag && completionTag[0] == '\0')
+ if (qc && qc->commandTag == COMMANDTAG_UNKNOWN)
{
- if (portal->commandTag)
- strcpy(completionTag, portal->commandTag);
- if (strcmp(completionTag, "SELECT") == 0)
- sprintf(completionTag, "SELECT 0 0");
- else if (strcmp(completionTag, "INSERT") == 0)
- strcpy(completionTag, "INSERT 0 0");
- else if (strcmp(completionTag, "UPDATE") == 0)
- strcpy(completionTag, "UPDATE 0");
- else if (strcmp(completionTag, "DELETE") == 0)
- strcpy(completionTag, "DELETE 0");
+ if (portal->qc.commandTag != COMMANDTAG_UNKNOWN)
+ CopyQueryCompletion(qc, &portal->qc);
+ /* If the caller supplied a qc, we should have set it by now. */
+ Assert(qc->commandTag != COMMANDTAG_UNKNOWN);
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index bb85b5e52a..b7b7c952a7 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -83,10 +83,9 @@ static void ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletion *qc);
static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
-
/*
* CommandIsReadOnly: is an executable query read-only?
*
@@ -467,7 +466,6 @@ CheckRestrictedOperation(const char *cmdname)
cmdname)));
}
-
/*
* ProcessUtility
* general utility function invoker
@@ -480,16 +478,13 @@ CheckRestrictedOperation(const char *cmdname)
* queryEnv: environment for parse through execution (e.g., ephemeral named
* tables like trigger transition tables). May be NULL.
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store command completion status data.
*
* Caller MUST supply a queryString; it is not allowed (anymore) to pass NULL.
* If you really don't have source text, you can pass a constant string,
* perhaps "(query not available)".
*
- * completionTag is only set nonempty if we want to return a nondefault status.
- *
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*
* Note for users of ProcessUtility_hook: the same queryString may be passed
* to multiple invocations of ProcessUtility when processing a query string
@@ -507,7 +502,7 @@ ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Assert(IsA(pstmt, PlannedStmt));
Assert(pstmt->commandType == CMD_UTILITY);
@@ -521,11 +516,11 @@ ProcessUtility(PlannedStmt *pstmt,
if (ProcessUtility_hook)
(*ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
/*
@@ -546,7 +541,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -562,18 +557,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (readonly_flags != COMMAND_IS_STRICTLY_READ_ONLY &&
(XactReadOnly || IsInParallelMode()))
{
- const char *commandtag = CreateCommandTag(parsetree);
+ CommandTag commandtag = CreateCommandTag(parsetree);
if ((readonly_flags & COMMAND_OK_IN_READ_ONLY_TXN) == 0)
- PreventCommandIfReadOnly(commandtag);
+ PreventCommandTagIfReadOnly(commandtag);
if ((readonly_flags & COMMAND_OK_IN_PARALLEL_MODE) == 0)
- PreventCommandIfParallelMode(commandtag);
+ PreventCommandTagIfParallelMode(commandtag);
if ((readonly_flags & COMMAND_OK_IN_RECOVERY) == 0)
- PreventCommandDuringRecovery(commandtag);
+ PreventCommandTagDuringRecovery(commandtag);
}
- if (completionTag)
- completionTag[0] = '\0';
+ if (qc)
+ InitializeQueryCompletion(qc);
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -623,18 +618,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case TRANS_STMT_COMMIT:
if (!EndTransactionBlock(stmt->chain))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQueryCompletion(qc, COMMANDTAG_ROLLBACK, 0);
}
break;
case TRANS_STMT_PREPARE:
if (!PrepareTransactionBlock(stmt->gid))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQueryCompletion(qc, COMMANDTAG_ROLLBACK, 0);
}
break;
@@ -693,8 +688,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case T_FetchStmt:
- PerformPortalFetch((FetchStmt *) parsetree, dest,
- completionTag);
+ PerformPortalFetch((FetchStmt *) parsetree, dest, qc);
break;
case T_DoStmt:
@@ -729,9 +723,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
DoCopy(pstate, (CopyStmt *) parsetree,
pstmt->stmt_location, pstmt->stmt_len,
&processed);
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "COPY " UINT64_FORMAT, processed);
+ if (qc)
+ SetQueryCompletion(qc, COMMANDTAG_COPY, processed);
}
break;
@@ -745,7 +738,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ExecuteQuery(pstate,
(ExecuteStmt *) parsetree, NULL,
params,
- dest, completionTag);
+ dest, qc);
break;
case T_DeallocateStmt:
@@ -974,7 +967,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecuteGrantStmt(stmt);
}
@@ -987,7 +980,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecDropStmt(stmt, isTopLevel);
}
@@ -1000,7 +993,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecRenameStmt(stmt);
}
@@ -1013,7 +1006,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectDependsStmt(stmt, NULL);
}
@@ -1026,7 +1019,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
}
@@ -1039,7 +1032,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterOwnerStmt(stmt);
}
@@ -1052,7 +1045,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
CommentObject(stmt);
break;
@@ -1065,7 +1058,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecSecLabelStmt(stmt);
break;
@@ -1075,7 +1068,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
break;
}
@@ -1102,7 +1095,7 @@ ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -1605,7 +1598,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateTableAsStmt:
address = ExecCreateTableAs(pstate, (CreateTableAsStmt *) parsetree,
- params, queryEnv, completionTag);
+ params, queryEnv, qc);
break;
case T_RefreshMatViewStmt:
@@ -1620,7 +1613,7 @@ ProcessUtilitySlow(ParseState *pstate,
PG_TRY();
{
address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
- queryString, params, completionTag);
+ queryString, params, qc);
}
PG_FINALLY();
{
@@ -2099,137 +2092,137 @@ UtilityContainsQuery(Node *parsetree)
*
* This covers most cases where ALTER is used with an ObjectType enum.
*/
-static const char *
+static CommandTag
AlterObjectTypeCommandTag(ObjectType objtype)
{
- const char *tag;
+ CommandTag tag;
switch (objtype)
{
case OBJECT_AGGREGATE:
- tag = "ALTER AGGREGATE";
+ tag = COMMANDTAG_ALTER_AGGREGATE;
break;
case OBJECT_ATTRIBUTE:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case OBJECT_CAST:
- tag = "ALTER CAST";
+ tag = COMMANDTAG_ALTER_CAST;
break;
case OBJECT_COLLATION:
- tag = "ALTER COLLATION";
+ tag = COMMANDTAG_ALTER_COLLATION;
break;
case OBJECT_COLUMN:
- tag = "ALTER TABLE";
+ tag = COMMANDTAG_ALTER_TABLE;
break;
case OBJECT_CONVERSION:
- tag = "ALTER CONVERSION";
+ tag = COMMANDTAG_ALTER_CONVERSION;
break;
case OBJECT_DATABASE:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case OBJECT_DOMAIN:
case OBJECT_DOMCONSTRAINT:
- tag = "ALTER DOMAIN";
+ tag = COMMANDTAG_ALTER_DOMAIN;
break;
case OBJECT_EXTENSION:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case OBJECT_FDW:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "ALTER SERVER";
+ tag = COMMANDTAG_ALTER_SERVER;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "ALTER FOREIGN TABLE";
+ tag = COMMANDTAG_ALTER_FOREIGN_TABLE;
break;
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = COMMANDTAG_ALTER_FUNCTION;
break;
case OBJECT_INDEX:
- tag = "ALTER INDEX";
+ tag = COMMANDTAG_ALTER_INDEX;
break;
case OBJECT_LANGUAGE:
- tag = "ALTER LANGUAGE";
+ tag = COMMANDTAG_ALTER_LANGUAGE;
break;
case OBJECT_LARGEOBJECT:
- tag = "ALTER LARGE OBJECT";
+ tag = COMMANDTAG_ALTER_LARGE_OBJECT;
break;
case OBJECT_OPCLASS:
- tag = "ALTER OPERATOR CLASS";
+ tag = COMMANDTAG_ALTER_OPERATOR_CLASS;
break;
case OBJECT_OPERATOR:
- tag = "ALTER OPERATOR";
+ tag = COMMANDTAG_ALTER_OPERATOR;
break;
case OBJECT_OPFAMILY:
- tag = "ALTER OPERATOR FAMILY";
+ tag = COMMANDTAG_ALTER_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "ALTER POLICY";
+ tag = COMMANDTAG_ALTER_POLICY;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = COMMANDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROLE:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = COMMANDTAG_ALTER_ROUTINE;
break;
case OBJECT_RULE:
- tag = "ALTER RULE";
+ tag = COMMANDTAG_ALTER_RULE;
break;
case OBJECT_SCHEMA:
- tag = "ALTER SCHEMA";
+ tag = COMMANDTAG_ALTER_SCHEMA;
break;
case OBJECT_SEQUENCE:
- tag = "ALTER SEQUENCE";
+ tag = COMMANDTAG_ALTER_SEQUENCE;
break;
case OBJECT_TABLE:
case OBJECT_TABCONSTRAINT:
- tag = "ALTER TABLE";
+ tag = COMMANDTAG_ALTER_TABLE;
break;
case OBJECT_TABLESPACE:
- tag = "ALTER TABLESPACE";
+ tag = COMMANDTAG_ALTER_TABLESPACE;
break;
case OBJECT_TRIGGER:
- tag = "ALTER TRIGGER";
+ tag = COMMANDTAG_ALTER_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "ALTER EVENT TRIGGER";
+ tag = COMMANDTAG_ALTER_EVENT_TRIGGER;
break;
case OBJECT_TSCONFIGURATION:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_TSDICTIONARY:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSPARSER:
- tag = "ALTER TEXT SEARCH PARSER";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSTEMPLATE:
- tag = "ALTER TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TYPE:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case OBJECT_VIEW:
- tag = "ALTER VIEW";
+ tag = COMMANDTAG_ALTER_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "ALTER MATERIALIZED VIEW";
+ tag = COMMANDTAG_ALTER_MATERIALIZED_VIEW;
break;
case OBJECT_PUBLICATION:
- tag = "ALTER PUBLICATION";
+ tag = COMMANDTAG_ALTER_PUBLICATION;
break;
case OBJECT_SUBSCRIPTION:
- tag = "ALTER SUBSCRIPTION";
+ tag = COMMANDTAG_ALTER_SUBSCRIPTION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "ALTER STATISTICS";
+ tag = COMMANDTAG_ALTER_STATISTICS;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
@@ -2238,20 +2231,17 @@ AlterObjectTypeCommandTag(ObjectType objtype)
/*
* CreateCommandTag
- * utility to get a string representation of the command operation,
+ * utility to get a CommandTag for the command operation,
* given either a raw (un-analyzed) parsetree, an analyzed Query,
* or a PlannedStmt.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
- *
- * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
- * Also, the result must point at a true constant (permanent storage).
*/
-const char *
+CommandTag
CreateCommandTag(Node *parsetree)
{
- const char *tag;
+ CommandTag tag;
switch (nodeTag(parsetree))
{
@@ -2262,19 +2252,19 @@ CreateCommandTag(Node *parsetree)
/* raw plannable queries */
case T_InsertStmt:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case T_DeleteStmt:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case T_UpdateStmt:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case T_SelectStmt:
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
/* utility statements --- same whether raw or cooked */
@@ -2285,51 +2275,51 @@ CreateCommandTag(Node *parsetree)
switch (stmt->kind)
{
case TRANS_STMT_BEGIN:
- tag = "BEGIN";
+ tag = COMMANDTAG_BEGIN;
break;
case TRANS_STMT_START:
- tag = "START TRANSACTION";
+ tag = COMMANDTAG_START_TRANSACTION;
break;
case TRANS_STMT_COMMIT:
- tag = "COMMIT";
+ tag = COMMANDTAG_COMMIT;
break;
case TRANS_STMT_ROLLBACK:
case TRANS_STMT_ROLLBACK_TO:
- tag = "ROLLBACK";
+ tag = COMMANDTAG_ROLLBACK;
break;
case TRANS_STMT_SAVEPOINT:
- tag = "SAVEPOINT";
+ tag = COMMANDTAG_SAVEPOINT;
break;
case TRANS_STMT_RELEASE:
- tag = "RELEASE";
+ tag = COMMANDTAG_RELEASE;
break;
case TRANS_STMT_PREPARE:
- tag = "PREPARE TRANSACTION";
+ tag = COMMANDTAG_PREPARE_TRANSACTION;
break;
case TRANS_STMT_COMMIT_PREPARED:
- tag = "COMMIT PREPARED";
+ tag = COMMANDTAG_COMMIT_PREPARED;
break;
case TRANS_STMT_ROLLBACK_PREPARED:
- tag = "ROLLBACK PREPARED";
+ tag = COMMANDTAG_ROLLBACK_PREPARED;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
break;
case T_DeclareCursorStmt:
- tag = "DECLARE CURSOR";
+ tag = COMMANDTAG_DECLARE_CURSOR;
break;
case T_ClosePortalStmt:
@@ -2337,9 +2327,9 @@ CreateCommandTag(Node *parsetree)
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
if (stmt->portalname == NULL)
- tag = "CLOSE CURSOR ALL";
+ tag = COMMANDTAG_CLOSE_CURSOR_ALL;
else
- tag = "CLOSE CURSOR";
+ tag = COMMANDTAG_CLOSE_CURSOR;
}
break;
@@ -2347,209 +2337,209 @@ CreateCommandTag(Node *parsetree)
{
FetchStmt *stmt = (FetchStmt *) parsetree;
- tag = (stmt->ismove) ? "MOVE" : "FETCH";
+ tag = (stmt->ismove) ? COMMANDTAG_MOVE : COMMANDTAG_FETCH;
}
break;
case T_CreateDomainStmt:
- tag = "CREATE DOMAIN";
+ tag = COMMANDTAG_CREATE_DOMAIN;
break;
case T_CreateSchemaStmt:
- tag = "CREATE SCHEMA";
+ tag = COMMANDTAG_CREATE_SCHEMA;
break;
case T_CreateStmt:
- tag = "CREATE TABLE";
+ tag = COMMANDTAG_CREATE_TABLE;
break;
case T_CreateTableSpaceStmt:
- tag = "CREATE TABLESPACE";
+ tag = COMMANDTAG_CREATE_TABLESPACE;
break;
case T_DropTableSpaceStmt:
- tag = "DROP TABLESPACE";
+ tag = COMMANDTAG_DROP_TABLESPACE;
break;
case T_AlterTableSpaceOptionsStmt:
- tag = "ALTER TABLESPACE";
+ tag = COMMANDTAG_ALTER_TABLESPACE;
break;
case T_CreateExtensionStmt:
- tag = "CREATE EXTENSION";
+ tag = COMMANDTAG_CREATE_EXTENSION;
break;
case T_AlterExtensionStmt:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case T_AlterExtensionContentsStmt:
- tag = "ALTER EXTENSION";
+ tag = COMMANDTAG_ALTER_EXTENSION;
break;
case T_CreateFdwStmt:
- tag = "CREATE FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_CREATE_FOREIGN_DATA_WRAPPER;
break;
case T_AlterFdwStmt:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case T_CreateForeignServerStmt:
- tag = "CREATE SERVER";
+ tag = COMMANDTAG_CREATE_SERVER;
break;
case T_AlterForeignServerStmt:
- tag = "ALTER SERVER";
+ tag = COMMANDTAG_ALTER_SERVER;
break;
case T_CreateUserMappingStmt:
- tag = "CREATE USER MAPPING";
+ tag = COMMANDTAG_CREATE_USER_MAPPING;
break;
case T_AlterUserMappingStmt:
- tag = "ALTER USER MAPPING";
+ tag = COMMANDTAG_ALTER_USER_MAPPING;
break;
case T_DropUserMappingStmt:
- tag = "DROP USER MAPPING";
+ tag = COMMANDTAG_DROP_USER_MAPPING;
break;
case T_CreateForeignTableStmt:
- tag = "CREATE FOREIGN TABLE";
+ tag = COMMANDTAG_CREATE_FOREIGN_TABLE;
break;
case T_ImportForeignSchemaStmt:
- tag = "IMPORT FOREIGN SCHEMA";
+ tag = COMMANDTAG_IMPORT_FOREIGN_SCHEMA;
break;
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_TABLE:
- tag = "DROP TABLE";
+ tag = COMMANDTAG_DROP_TABLE;
break;
case OBJECT_SEQUENCE:
- tag = "DROP SEQUENCE";
+ tag = COMMANDTAG_DROP_SEQUENCE;
break;
case OBJECT_VIEW:
- tag = "DROP VIEW";
+ tag = COMMANDTAG_DROP_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "DROP MATERIALIZED VIEW";
+ tag = COMMANDTAG_DROP_MATERIALIZED_VIEW;
break;
case OBJECT_INDEX:
- tag = "DROP INDEX";
+ tag = COMMANDTAG_DROP_INDEX;
break;
case OBJECT_TYPE:
- tag = "DROP TYPE";
+ tag = COMMANDTAG_DROP_TYPE;
break;
case OBJECT_DOMAIN:
- tag = "DROP DOMAIN";
+ tag = COMMANDTAG_DROP_DOMAIN;
break;
case OBJECT_COLLATION:
- tag = "DROP COLLATION";
+ tag = COMMANDTAG_DROP_COLLATION;
break;
case OBJECT_CONVERSION:
- tag = "DROP CONVERSION";
+ tag = COMMANDTAG_DROP_CONVERSION;
break;
case OBJECT_SCHEMA:
- tag = "DROP SCHEMA";
+ tag = COMMANDTAG_DROP_SCHEMA;
break;
case OBJECT_TSPARSER:
- tag = "DROP TEXT SEARCH PARSER";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "DROP TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "DROP TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "DROP TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_DROP_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "DROP FOREIGN TABLE";
+ tag = COMMANDTAG_DROP_FOREIGN_TABLE;
break;
case OBJECT_EXTENSION:
- tag = "DROP EXTENSION";
+ tag = COMMANDTAG_DROP_EXTENSION;
break;
case OBJECT_FUNCTION:
- tag = "DROP FUNCTION";
+ tag = COMMANDTAG_DROP_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "DROP PROCEDURE";
+ tag = COMMANDTAG_DROP_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "DROP ROUTINE";
+ tag = COMMANDTAG_DROP_ROUTINE;
break;
case OBJECT_AGGREGATE:
- tag = "DROP AGGREGATE";
+ tag = COMMANDTAG_DROP_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "DROP OPERATOR";
+ tag = COMMANDTAG_DROP_OPERATOR;
break;
case OBJECT_LANGUAGE:
- tag = "DROP LANGUAGE";
+ tag = COMMANDTAG_DROP_LANGUAGE;
break;
case OBJECT_CAST:
- tag = "DROP CAST";
+ tag = COMMANDTAG_DROP_CAST;
break;
case OBJECT_TRIGGER:
- tag = "DROP TRIGGER";
+ tag = COMMANDTAG_DROP_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "DROP EVENT TRIGGER";
+ tag = COMMANDTAG_DROP_EVENT_TRIGGER;
break;
case OBJECT_RULE:
- tag = "DROP RULE";
+ tag = COMMANDTAG_DROP_RULE;
break;
case OBJECT_FDW:
- tag = "DROP FOREIGN DATA WRAPPER";
+ tag = COMMANDTAG_DROP_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "DROP SERVER";
+ tag = COMMANDTAG_DROP_SERVER;
break;
case OBJECT_OPCLASS:
- tag = "DROP OPERATOR CLASS";
+ tag = COMMANDTAG_DROP_OPERATOR_CLASS;
break;
case OBJECT_OPFAMILY:
- tag = "DROP OPERATOR FAMILY";
+ tag = COMMANDTAG_DROP_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "DROP POLICY";
+ tag = COMMANDTAG_DROP_POLICY;
break;
case OBJECT_TRANSFORM:
- tag = "DROP TRANSFORM";
+ tag = COMMANDTAG_DROP_TRANSFORM;
break;
case OBJECT_ACCESS_METHOD:
- tag = "DROP ACCESS METHOD";
+ tag = COMMANDTAG_DROP_ACCESS_METHOD;
break;
case OBJECT_PUBLICATION:
- tag = "DROP PUBLICATION";
+ tag = COMMANDTAG_DROP_PUBLICATION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "DROP STATISTICS";
+ tag = COMMANDTAG_DROP_STATISTICS;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_TruncateStmt:
- tag = "TRUNCATE TABLE";
+ tag = COMMANDTAG_TRUNCATE_TABLE;
break;
case T_CommentStmt:
- tag = "COMMENT";
+ tag = COMMANDTAG_COMMENT;
break;
case T_SecLabelStmt:
- tag = "SECURITY LABEL";
+ tag = COMMANDTAG_SECURITY_LABEL;
break;
case T_CopyStmt:
- tag = "COPY";
+ tag = COMMANDTAG_COPY;
break;
case T_RenameStmt:
@@ -2584,23 +2574,23 @@ CreateCommandTag(Node *parsetree)
break;
case T_AlterDomainStmt:
- tag = "ALTER DOMAIN";
+ tag = COMMANDTAG_ALTER_DOMAIN;
break;
case T_AlterFunctionStmt:
switch (((AlterFunctionStmt *) parsetree)->objtype)
{
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = COMMANDTAG_ALTER_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = COMMANDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = COMMANDTAG_ALTER_ROUTINE;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
@@ -2608,7 +2598,7 @@ CreateCommandTag(Node *parsetree)
{
GrantStmt *stmt = (GrantStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
+ tag = (stmt->is_grant) ? COMMANDTAG_GRANT : COMMANDTAG_REVOKE;
}
break;
@@ -2616,145 +2606,145 @@ CreateCommandTag(Node *parsetree)
{
GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
+ tag = (stmt->is_grant) ? COMMANDTAG_GRANT_ROLE : COMMANDTAG_REVOKE_ROLE;
}
break;
case T_AlterDefaultPrivilegesStmt:
- tag = "ALTER DEFAULT PRIVILEGES";
+ tag = COMMANDTAG_ALTER_DEFAULT_PRIVILEGES;
break;
case T_DefineStmt:
switch (((DefineStmt *) parsetree)->kind)
{
case OBJECT_AGGREGATE:
- tag = "CREATE AGGREGATE";
+ tag = COMMANDTAG_CREATE_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "CREATE OPERATOR";
+ tag = COMMANDTAG_CREATE_OPERATOR;
break;
case OBJECT_TYPE:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case OBJECT_TSPARSER:
- tag = "CREATE TEXT SEARCH PARSER";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "CREATE TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "CREATE TEXT SEARCH TEMPLATE";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "CREATE TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_CREATE_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_COLLATION:
- tag = "CREATE COLLATION";
+ tag = COMMANDTAG_CREATE_COLLATION;
break;
case OBJECT_ACCESS_METHOD:
- tag = "CREATE ACCESS METHOD";
+ tag = COMMANDTAG_CREATE_ACCESS_METHOD;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_CompositeTypeStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_CreateEnumStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_CreateRangeStmt:
- tag = "CREATE TYPE";
+ tag = COMMANDTAG_CREATE_TYPE;
break;
case T_AlterEnumStmt:
- tag = "ALTER TYPE";
+ tag = COMMANDTAG_ALTER_TYPE;
break;
case T_ViewStmt:
- tag = "CREATE VIEW";
+ tag = COMMANDTAG_CREATE_VIEW;
break;
case T_CreateFunctionStmt:
if (((CreateFunctionStmt *) parsetree)->is_procedure)
- tag = "CREATE PROCEDURE";
+ tag = COMMANDTAG_CREATE_PROCEDURE;
else
- tag = "CREATE FUNCTION";
+ tag = COMMANDTAG_CREATE_FUNCTION;
break;
case T_IndexStmt:
- tag = "CREATE INDEX";
+ tag = COMMANDTAG_CREATE_INDEX;
break;
case T_RuleStmt:
- tag = "CREATE RULE";
+ tag = COMMANDTAG_CREATE_RULE;
break;
case T_CreateSeqStmt:
- tag = "CREATE SEQUENCE";
+ tag = COMMANDTAG_CREATE_SEQUENCE;
break;
case T_AlterSeqStmt:
- tag = "ALTER SEQUENCE";
+ tag = COMMANDTAG_ALTER_SEQUENCE;
break;
case T_DoStmt:
- tag = "DO";
+ tag = COMMANDTAG_DO;
break;
case T_CreatedbStmt:
- tag = "CREATE DATABASE";
+ tag = COMMANDTAG_CREATE_DATABASE;
break;
case T_AlterDatabaseStmt:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case T_AlterDatabaseSetStmt:
- tag = "ALTER DATABASE";
+ tag = COMMANDTAG_ALTER_DATABASE;
break;
case T_DropdbStmt:
- tag = "DROP DATABASE";
+ tag = COMMANDTAG_DROP_DATABASE;
break;
case T_NotifyStmt:
- tag = "NOTIFY";
+ tag = COMMANDTAG_NOTIFY;
break;
case T_ListenStmt:
- tag = "LISTEN";
+ tag = COMMANDTAG_LISTEN;
break;
case T_UnlistenStmt:
- tag = "UNLISTEN";
+ tag = COMMANDTAG_UNLISTEN;
break;
case T_LoadStmt:
- tag = "LOAD";
+ tag = COMMANDTAG_LOAD;
break;
case T_CallStmt:
- tag = "CALL";
+ tag = COMMANDTAG_CALL;
break;
case T_ClusterStmt:
- tag = "CLUSTER";
+ tag = COMMANDTAG_CLUSTER;
break;
case T_VacuumStmt:
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
- tag = "VACUUM";
+ tag = COMMANDTAG_VACUUM;
else
- tag = "ANALYZE";
+ tag = COMMANDTAG_ANALYZE;
break;
case T_ExplainStmt:
- tag = "EXPLAIN";
+ tag = COMMANDTAG_EXPLAIN;
break;
case T_CreateTableAsStmt:
@@ -2762,24 +2752,24 @@ CreateCommandTag(Node *parsetree)
{
case OBJECT_TABLE:
if (((CreateTableAsStmt *) parsetree)->is_select_into)
- tag = "SELECT INTO";
+ tag = COMMANDTAG_SELECT_INTO;
else
- tag = "CREATE TABLE AS";
+ tag = COMMANDTAG_CREATE_TABLE_AS;
break;
case OBJECT_MATVIEW:
- tag = "CREATE MATERIALIZED VIEW";
+ tag = COMMANDTAG_CREATE_MATERIALIZED_VIEW;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_RefreshMatViewStmt:
- tag = "REFRESH MATERIALIZED VIEW";
+ tag = COMMANDTAG_REFRESH_MATERIALIZED_VIEW;
break;
case T_AlterSystemStmt:
- tag = "ALTER SYSTEM";
+ tag = COMMANDTAG_ALTER_SYSTEM;
break;
case T_VariableSetStmt:
@@ -2789,183 +2779,183 @@ CreateCommandTag(Node *parsetree)
case VAR_SET_CURRENT:
case VAR_SET_DEFAULT:
case VAR_SET_MULTI:
- tag = "SET";
+ tag = COMMANDTAG_SET;
break;
case VAR_RESET:
case VAR_RESET_ALL:
- tag = "RESET";
+ tag = COMMANDTAG_RESET;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_VariableShowStmt:
- tag = "SHOW";
+ tag = COMMANDTAG_SHOW;
break;
case T_DiscardStmt:
switch (((DiscardStmt *) parsetree)->target)
{
case DISCARD_ALL:
- tag = "DISCARD ALL";
+ tag = COMMANDTAG_DISCARD_ALL;
break;
case DISCARD_PLANS:
- tag = "DISCARD PLANS";
+ tag = COMMANDTAG_DISCARD_PLANS;
break;
case DISCARD_TEMP:
- tag = "DISCARD TEMP";
+ tag = COMMANDTAG_DISCARD_TEMP;
break;
case DISCARD_SEQUENCES:
- tag = "DISCARD SEQUENCES";
+ tag = COMMANDTAG_DISCARD_SEQUENCES;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
}
break;
case T_CreateTransformStmt:
- tag = "CREATE TRANSFORM";
+ tag = COMMANDTAG_CREATE_TRANSFORM;
break;
case T_CreateTrigStmt:
- tag = "CREATE TRIGGER";
+ tag = COMMANDTAG_CREATE_TRIGGER;
break;
case T_CreateEventTrigStmt:
- tag = "CREATE EVENT TRIGGER";
+ tag = COMMANDTAG_CREATE_EVENT_TRIGGER;
break;
case T_AlterEventTrigStmt:
- tag = "ALTER EVENT TRIGGER";
+ tag = COMMANDTAG_ALTER_EVENT_TRIGGER;
break;
case T_CreatePLangStmt:
- tag = "CREATE LANGUAGE";
+ tag = COMMANDTAG_CREATE_LANGUAGE;
break;
case T_CreateRoleStmt:
- tag = "CREATE ROLE";
+ tag = COMMANDTAG_CREATE_ROLE;
break;
case T_AlterRoleStmt:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case T_AlterRoleSetStmt:
- tag = "ALTER ROLE";
+ tag = COMMANDTAG_ALTER_ROLE;
break;
case T_DropRoleStmt:
- tag = "DROP ROLE";
+ tag = COMMANDTAG_DROP_ROLE;
break;
case T_DropOwnedStmt:
- tag = "DROP OWNED";
+ tag = COMMANDTAG_DROP_OWNED;
break;
case T_ReassignOwnedStmt:
- tag = "REASSIGN OWNED";
+ tag = COMMANDTAG_REASSIGN_OWNED;
break;
case T_LockStmt:
- tag = "LOCK TABLE";
+ tag = COMMANDTAG_LOCK_TABLE;
break;
case T_ConstraintsSetStmt:
- tag = "SET CONSTRAINTS";
+ tag = COMMANDTAG_SET_CONSTRAINTS;
break;
case T_CheckPointStmt:
- tag = "CHECKPOINT";
+ tag = COMMANDTAG_CHECKPOINT;
break;
case T_ReindexStmt:
- tag = "REINDEX";
+ tag = COMMANDTAG_REINDEX;
break;
case T_CreateConversionStmt:
- tag = "CREATE CONVERSION";
+ tag = COMMANDTAG_CREATE_CONVERSION;
break;
case T_CreateCastStmt:
- tag = "CREATE CAST";
+ tag = COMMANDTAG_CREATE_CAST;
break;
case T_CreateOpClassStmt:
- tag = "CREATE OPERATOR CLASS";
+ tag = COMMANDTAG_CREATE_OPERATOR_CLASS;
break;
case T_CreateOpFamilyStmt:
- tag = "CREATE OPERATOR FAMILY";
+ tag = COMMANDTAG_CREATE_OPERATOR_FAMILY;
break;
case T_AlterOpFamilyStmt:
- tag = "ALTER OPERATOR FAMILY";
+ tag = COMMANDTAG_ALTER_OPERATOR_FAMILY;
break;
case T_AlterOperatorStmt:
- tag = "ALTER OPERATOR";
+ tag = COMMANDTAG_ALTER_OPERATOR;
break;
case T_AlterTSDictionaryStmt:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case T_AlterTSConfigurationStmt:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = COMMANDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case T_CreatePolicyStmt:
- tag = "CREATE POLICY";
+ tag = COMMANDTAG_CREATE_POLICY;
break;
case T_AlterPolicyStmt:
- tag = "ALTER POLICY";
+ tag = COMMANDTAG_ALTER_POLICY;
break;
case T_CreateAmStmt:
- tag = "CREATE ACCESS METHOD";
+ tag = COMMANDTAG_CREATE_ACCESS_METHOD;
break;
case T_CreatePublicationStmt:
- tag = "CREATE PUBLICATION";
+ tag = COMMANDTAG_CREATE_PUBLICATION;
break;
case T_AlterPublicationStmt:
- tag = "ALTER PUBLICATION";
+ tag = COMMANDTAG_ALTER_PUBLICATION;
break;
case T_CreateSubscriptionStmt:
- tag = "CREATE SUBSCRIPTION";
+ tag = COMMANDTAG_CREATE_SUBSCRIPTION;
break;
case T_AlterSubscriptionStmt:
- tag = "ALTER SUBSCRIPTION";
+ tag = COMMANDTAG_ALTER_SUBSCRIPTION;
break;
case T_DropSubscriptionStmt:
- tag = "DROP SUBSCRIPTION";
+ tag = COMMANDTAG_DROP_SUBSCRIPTION;
break;
case T_AlterCollationStmt:
- tag = "ALTER COLLATION";
+ tag = COMMANDTAG_ALTER_COLLATION;
break;
case T_PrepareStmt:
- tag = "PREPARE";
+ tag = COMMANDTAG_PREPARE;
break;
case T_ExecuteStmt:
- tag = "EXECUTE";
+ tag = COMMANDTAG_EXECUTE;
break;
case T_CreateStatsStmt:
- tag = "CREATE STATISTICS";
+ tag = COMMANDTAG_CREATE_STATISTICS;
break;
case T_AlterStatsStmt:
- tag = "ALTER STATISTICS";
+ tag = COMMANDTAG_ALTER_STATISTICS;
break;
case T_DeallocateStmt:
@@ -2973,9 +2963,9 @@ CreateCommandTag(Node *parsetree)
DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
if (stmt->name == NULL)
- tag = "DEALLOCATE ALL";
+ tag = COMMANDTAG_DEALLOCATE_ALL;
else
- tag = "DEALLOCATE";
+ tag = COMMANDTAG_DEALLOCATE;
}
break;
@@ -2999,33 +2989,33 @@ CreateCommandTag(Node *parsetree)
switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = COMMANDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = COMMANDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
}
}
else
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3033,7 +3023,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
@@ -3059,33 +3049,33 @@ CreateCommandTag(Node *parsetree)
switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = COMMANDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = COMMANDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = COMMANDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
else
- tag = "SELECT";
+ tag = COMMANDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = COMMANDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = COMMANDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = COMMANDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3093,7 +3083,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
}
@@ -3102,7 +3092,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
- tag = "???";
+ tag = COMMANDTAG_UNKNOWN;
break;
}
diff --git a/src/backend/utils/.gitignore b/src/backend/utils/.gitignore
index 0685556959..7d6dfa45a5 100644
--- a/src/backend/utils/.gitignore
+++ b/src/backend/utils/.gitignore
@@ -1,6 +1,9 @@
+/commandtag_behavior.h
+/commandtag_enum.h
/fmgrtab.c
/fmgroids.h
/fmgrprotos.h
/fmgr-stamp
+/commandtag-stamp
/probes.h
/errcodes.h
diff --git a/src/backend/utils/Makefile b/src/backend/utils/Makefile
index b91028ddfd..3c433cf249 100644
--- a/src/backend/utils/Makefile
+++ b/src/backend/utils/Makefile
@@ -13,7 +13,7 @@ subdir = src/backend/utils
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-OBJS = fmgrtab.o
+OBJS = commandtag.o fmgrtab.o
SUBDIRS = adt cache error fmgr hash init mb misc mmgr resowner sort time
# location of Catalog.pm
@@ -23,13 +23,13 @@ include $(top_srcdir)/src/backend/common.mk
all: distprep probes.h generated-header-symlinks
-distprep: fmgr-stamp errcodes.h
+distprep: commandtag-stamp fmgr-stamp errcodes.h
.PHONY: generated-header-symlinks
generated-header-symlinks: $(top_builddir)/src/include/utils/header-stamp $(top_builddir)/src/include/utils/probes.h
-$(SUBDIRS:%=%-recursive): fmgr-stamp errcodes.h
+$(SUBDIRS:%=%-recursive): commandtag-stamp fmgr-stamp errcodes.h
# fmgr-stamp records the last time we ran Gen_fmgrtab.pl. We don't rely on
# the timestamps of the individual output files, because the Perl script
@@ -38,6 +38,10 @@ fmgr-stamp: Gen_fmgrtab.pl $(catalogdir)/Catalog.pm $(top_srcdir)/src/include/ca
$(PERL) -I $(catalogdir) $< --include-path=$(top_srcdir)/src/include/ $(top_srcdir)/src/include/catalog/pg_proc.dat
touch $@
+commandtag-stamp: gencommandtag.pl $(top_srcdir)/src/include/utils/commandtag.dat
+ $(PERL) $<
+ touch $@
+
errcodes.h: $(top_srcdir)/src/backend/utils/errcodes.txt generate-errcodes.pl
$(PERL) $(srcdir)/generate-errcodes.pl $< > $@
@@ -62,10 +66,10 @@ endif
# These generated headers must be symlinked into builddir/src/include/,
# using absolute links for the reasons explained in src/backend/Makefile.
# We use header-stamp to record that we've done this because the symlinks
-# themselves may appear older than fmgr-stamp.
-$(top_builddir)/src/include/utils/header-stamp: fmgr-stamp errcodes.h
+# themselves may appear older than commandtag-stamp and fmgr-stamp.
+$(top_builddir)/src/include/utils/header-stamp: commandtag-stamp fmgr-stamp errcodes.h
prereqdir=`cd '$(dir $<)' >/dev/null && pwd` && \
- cd '$(dir $@)' && for file in fmgroids.h fmgrprotos.h errcodes.h; do \
+ cd '$(dir $@)' && for file in commandtag_enum.h commandtag_behavior.h fmgroids.h fmgrprotos.h errcodes.h; do \
rm -f $$file && $(LN_S) "$$prereqdir/$$file" . ; \
done
touch $@
@@ -87,10 +91,10 @@ installdirs:
uninstall-data:
rm -f $(addprefix '$(DESTDIR)$(datadir)'/, errcodes.txt)
-# fmgroids.h, fmgrprotos.h, fmgrtab.c, fmgr-stamp, and errcodes.h are in the
+# fmgroids.h, fmgrprotos.h, fmgrtab.c, commandtag-stamp, fmgr-stamp, and errcodes.h are in the
# distribution tarball, so they are not cleaned here.
clean:
rm -f probes.h
maintainer-clean: clean
- rm -f fmgroids.h fmgrprotos.h fmgrtab.c fmgr-stamp errcodes.h
+ rm -f commandtag_enum.h commandtag_behavior.h commandtag-stamp fmgroids.h fmgrprotos.h fmgrtab.c fmgr-stamp errcodes.h
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 1b63048a77..a960d7ca80 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -51,7 +51,7 @@ static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD;
static void BuildEventTriggerCache(void);
static void InvalidateEventCacheCallback(Datum arg,
int cacheid, uint32 hashvalue);
-static int DecodeTextArrayToCString(Datum array, char ***cstringp);
+static void DecodeTextArrayToBitmapset(Datum array, Bitmapset **bms);
/*
* Search the event cache by trigger event.
@@ -180,10 +180,7 @@ BuildEventTriggerCache(void)
evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags,
RelationGetDescr(rel), &evttags_isnull);
if (!evttags_isnull)
- {
- item->ntags = DecodeTextArrayToCString(evttags, &item->tag);
- qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp);
- }
+ DecodeTextArrayToBitmapset(evttags, &item->tagset);
/* Add to cache entry. */
entry = hash_search(cache, &event, HASH_ENTER, &found);
@@ -215,18 +212,18 @@ BuildEventTriggerCache(void)
}
/*
- * Decode text[] to an array of C strings.
+ * Decode text[] to a Bitmapset of CommandTags.
*
* We could avoid a bit of overhead here if we were willing to duplicate some
* of the logic from deconstruct_array, but it doesn't seem worth the code
* complexity.
*/
-static int
-DecodeTextArrayToCString(Datum array, char ***cstringp)
+static void
+DecodeTextArrayToBitmapset(Datum array, Bitmapset **tagset)
{
ArrayType *arr = DatumGetArrayTypeP(array);
Datum *elems;
- char **cstring;
+ Bitmapset *bms;
int i;
int nelems;
@@ -234,13 +231,15 @@ DecodeTextArrayToCString(Datum array, char ***cstringp)
elog(ERROR, "expected 1-D text array");
deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems);
- cstring = palloc(nelems * sizeof(char *));
- for (i = 0; i < nelems; ++i)
- cstring[i] = TextDatumGetCString(elems[i]);
+ for (bms = NULL, i = 0; i < nelems; ++i)
+ {
+ char *str = TextDatumGetCString(elems[i]);
+ bms = bms_add_member(bms, GetCommandTagEnum(str));
+ pfree(str);
+ }
pfree(elems);
- *cstringp = cstring;
- return nelems;
+ *tagset = bms;
}
/*
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index c47be0ba4c..53401bd4e9 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -163,7 +163,7 @@ InitPlanCache(void)
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
MemoryContext source_context;
@@ -246,7 +246,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
CachedPlanSource *
CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
diff --git a/src/backend/utils/commandtag.c b/src/backend/utils/commandtag.c
new file mode 100644
index 0000000000..c629eca608
--- /dev/null
+++ b/src/backend/utils/commandtag.c
@@ -0,0 +1,119 @@
+/*-------------------------------------------------------------------------
+ *
+ * commandtag.c
+ * Data and routines for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/utils/commandtag.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "c.h"
+#include "miscadmin.h"
+#include "utils/commandtag.h"
+#include "utils/commandtag_enum.h"
+#include "utils/commandtag_behavior.h"
+
+static const unsigned int tag_behavior_length = lengthof(tag_behavior);
+
+static inline bool
+PG_VALID_COMMANDTAG(CommandTag commandTag)
+{
+ return (commandTag >= FIRST_COMMANDTAG &&
+ commandTag <= LAST_COMMANDTAG);
+}
+
+void
+InitializeQueryCompletion(QueryCompletion *qc)
+{
+ qc->commandTag = COMMANDTAG_UNKNOWN;
+ qc->nprocessed = 0;
+}
+
+const char *
+GetCommandTagName(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].name;
+}
+
+bool
+command_tag_display_last_oid(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].display_last_oid;
+}
+
+bool
+command_tag_display_rowcount(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].display_rowcount;
+}
+
+bool
+command_tag_event_trigger_ok(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].event_trigger_ok;
+}
+
+bool
+command_tag_table_rewrite_ok(CommandTag commandTag)
+{
+ Assert(PG_VALID_COMMANDTAG(commandTag));
+ return tag_behavior[commandTag].table_rewrite_ok;
+}
+
+/*
+ * Search CommandTag by name
+ *
+ * Returns CommandTag, or COMMANDTAG_UNKNOWN if not recognized
+ */
+CommandTag
+GetCommandTagEnum(const char *commandname)
+{
+ const CommandTagBehavior *base, *last, *position;
+ int result;
+
+ if (commandname == NULL || *commandname == '\0')
+ return COMMANDTAG_UNKNOWN;
+
+ base = tag_behavior;
+ last = tag_behavior + tag_behavior_length - 1;
+ while (last >= base)
+ {
+ position = base + ((last - base) >> 1);
+ result = pg_strcasecmp(commandname, position->name);
+ if (result == 0)
+ return (CommandTag) (position - tag_behavior);
+ else if (result < 0)
+ last = position - 1;
+ else
+ base = position + 1;
+ }
+ return COMMANDTAG_UNKNOWN;
+}
+
+void
+PreventCommandTagIfReadOnly(CommandTag commandTag)
+{
+ PreventCommandIfReadOnly(GetCommandTagName(commandTag));
+}
+
+void
+PreventCommandTagIfParallelMode(CommandTag commandTag)
+{
+ PreventCommandIfParallelMode(GetCommandTagName(commandTag));
+}
+
+void
+PreventCommandTagDuringRecovery(CommandTag commandTag)
+{
+ PreventCommandDuringRecovery(GetCommandTagName(commandTag));
+}
diff --git a/src/backend/utils/gencommandtag.pl b/src/backend/utils/gencommandtag.pl
new file mode 100755
index 0000000000..434f165305
--- /dev/null
+++ b/src/backend/utils/gencommandtag.pl
@@ -0,0 +1,266 @@
+#!/usr/bin/perl -w
+#----------------------------------------------------------------------
+#
+# gencommandtag.pl
+# Perl script that generates the commandtag data headers from the
+# specially formatted commandtag.dat data file.
+#
+# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/backend/utils/gencommandtag.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+use File::Basename;
+use File::Spec;
+BEGIN { use lib File::Spec->rel2abs(dirname(__FILE__)); }
+
+use FindBin;
+use lib "$FindBin::RealBin/../catalog/";
+use DataFile;
+
+my $outputdir = '.';
+my $inputfile = '../../include/utils/commandtag.dat';
+
+GetOptions(
+ 'inputfile:s' => \$inputfile,
+ 'outputdir:s' => \$outputdir) || usage();
+
+# Sanity check arguments.
+die "input file $inputfile does not exist" unless(-f $inputfile);
+die "output directory $outputdir does not exist" unless(-d $outputdir);
+
+# Make sure paths end with a slash.
+$outputdir .= '/' if (substr($outputdir, -1) ne '/');
+
+my $enumfile = join('', $outputdir, "commandtag_enum.h");
+my $arrayfile = join('', $outputdir, "commandtag_behavior.h");
+
+# Vivify the input data, but be paranoid about eval'ing arbitrary code
+my $data = DataFile::ParseData($inputfile);
+sanity_check_data($data);
+
+# Sort the commandtags based on their stringified representation,
+# not their labels.
+my @sorted = sort { $a->{tagname} cmp $b->{tagname} } @$data;
+
+# Generate all output internally before outputting anything, to avoid
+# partially overwriting generated files under error conditions
+my (@enum_chunks, @behavior_chunks);
+push(@enum_chunks,
+ boilerplate_header("commandtag_enum.h",
+ "Enumeration of all PostgreSQL command tags.",
+ "src/include/utils/commandtag_enum.h",
+ "COMMANDTAG_ENUM_H"));
+
+push (@enum_chunks, qq(
+typedef enum CommandTag
+{
+#define FIRST_COMMANDTAG COMMANDTAG_$sorted[0]->{taglabel})
+);
+
+push(@behavior_chunks,
+ boilerplate_header("commandtag_behavior.h",
+ "Array of records defining the behavior of each command tag.",
+ "src/include/utils/commandtag_behavior.h",
+ "COMMANDTAG_BEHAVIOR_H"));
+push (@behavior_chunks, q(
+const CommandTagBehavior tag_behavior[] = {)
+);
+
+for(my $i = 0; $i < scalar(@sorted); $i++)
+{
+ my $rec = $sorted[$i];
+ my $comma = ($i < $#sorted) ? "," : "";
+ my $enumrow = sprintf("\tCOMMANDTAG_%s%s", $rec->{taglabel}, $comma);
+ push(@enum_chunks, $enumrow);
+
+ my $behaviorrow = sprintf("\t[COMMANDTAG_%s] = {", $rec->{taglabel});
+ $behaviorrow .= sprintf("\n\t\t.name = \"%s\"", $rec->{tagname});
+ $behaviorrow .= ",\n\t\t.event_trigger_ok = true" if ($rec->{event_trigger_ok});
+ $behaviorrow .= ",\n\t\t.table_rewrite_ok = true" if ($rec->{table_rewrite_ok});
+ $behaviorrow .= ",\n\t\t.display_rowcount = true" if ($rec->{display_rowcount});
+ $behaviorrow .= ",\n\t\t.display_last_oid = true" if ($rec->{display_last_oid});
+ $behaviorrow .= "\n\t}$comma";
+ push (@behavior_chunks, $behaviorrow);
+}
+
+push(@enum_chunks,
+qq(#define LAST_COMMANDTAG COMMANDTAG_$sorted[$#sorted]->{taglabel}
+} CommandTag;),
+ boilerplate_footer("COMMANDTAG_ENUM_H")
+ );
+
+push(@behavior_chunks,
+q(};
+StaticAssertDecl(FIRST_COMMANDTAG == (CommandTag)0, "first command tag");
+StaticAssertDecl(lengthof(tag_behavior) == (LAST_COMMANDTAG + 1),
+ "array length mismatch");),
+ boilerplate_footer("COMMANDTAG_BEHAVIOR_H")
+ );
+
+my $enum_out = IO::File->new(">$enumfile")
+ or die "Cannot write $enumfile: $!";
+$enum_out->print(join("\n", @enum_chunks));
+$enum_out->close();
+
+my $array_out = IO::File->new(">$arrayfile")
+ or die "Cannot write $arrayfile: $!";
+$array_out->print(join("\n", @behavior_chunks));
+$array_out->close();
+
+# Check that the records from the data file look reasonable.
+#
+# Some of the checks here cannot fail, because they check things that were
+# already checked before eval'ing the inputfile data. We check anyway, for
+# future-proofing and because the prior checks were against the stringified
+# form of the data and these checks are against the perl data structures that
+# we revivified, and the possibility of subtle corner cases that entails.
+#
+sub sanity_check_data
+{
+ my ($data) = @_;
+
+ # Verify that at the outermost layer the data file is an array reference
+ dataerror("Malformed input data: expected an array reference",
+ "The input file should start with a '[' and end with a ']'")
+ unless defined $data and ref($data) and ref($data) =~ m/ARRAY/;
+
+ # Verify that the data array contains only hash references
+ dataerror("Malformed input data: expected an array of hash references",
+ "The input file array should contain only records in perl hash " .
+ "reference '{...}' format")
+ if (grep { !ref($_) || ref($_) !~ m/HASH/ } @$data);
+
+ my @required_keys = qw(tagname taglabel);
+ my @flag_keys = qw(event_trigger_ok table_rewrite_ok display_rowcount
+ display_last_oid);
+ my @expected_keys = (@required_keys, @flag_keys);
+ my %expected_keys = map { $_ => 1 } @expected_keys;
+
+ # Check that each record in the data file contains the required
+ # fields. If we have only one required field (name or label) but
+ # not the other, we can use the one we have in the error message
+ # to help direct the user to the record which needs correcting.
+ foreach my $href (@$data)
+ {
+ my $preamble = "Malformed input data:";
+ if (defined $href->{taglabel} and length $href->{taglabel})
+ {
+ $preamble .= " Record for commandtag label '$href->{taglabel}':";
+ }
+ elsif (defined $href->{tagname} and length $href->{tagname})
+ {
+ $preamble .= " Record for commandtag name '$href->{tagname}':";
+ }
+
+ foreach my $key (@required_keys)
+ {
+ dataerror("$preamble missing '$key'",
+ "Each record in the data file should fields for @required_keys")
+ unless(exists $href->{$key});
+ dataerror("$preamble trivial value for '$key'")
+ unless(defined $href->{$key} and length $href->{$key});
+ dataerror("$preamble value of '$key' not uppercase: $href->{$key}")
+ unless($href->{$key} eq uc($href->{$key}));
+ }
+
+ foreach my $key (keys %$href)
+ {
+ dataerror("$preamble non-printable value for '$key'",
+ "Do not embed non-printable characters such as tabs, form-feeds or newlines")
+ if (grep { ord($_) < 0x20 } split(//, $href->{$key}));
+ dataerror("$preamble non-ASCII value for '$key'",
+ "Only printable 7-bit ASCII characters are allowed as commandtag data")
+ if (grep { ord($_) > 0x7F } split(//, $href->{$key}));
+ dataerror("$preamble unrecognized field '$key'",
+ "Recognized keys are @expected_keys")
+ unless $expected_keys{$key};
+ }
+
+ foreach my $flag (grep { exists $href->{$_} } @flag_keys)
+ {
+ my $val = $href->{$flag};
+ dataerror("$preamble empty or undefined value for '$flag'",
+ "Fields with no value should simply be omitted")
+ unless defined $val and length $val;
+ dataerror("$preamble false value need not be specified for '$flag'",
+ "Fields with false values should simply be omitted")
+ if ($val =~ m/^(?:false|f|0|off)$/i);
+ dataerror("$preamble unrecognized value for '$flag': '$val'")
+ unless ($val =~ m/^(?:true|t|1|on)$/i);
+ }
+ }
+}
+
+sub boilerplate_header
+{
+ my ($filename, $filedesc, $repo_path, $header_guard) = @_;
+ my $year = (localtime(time))[5] + 1900;
+ return
+"/*-------------------------------------------------------------------------
+ *
+ * $filename
+ * $filedesc
+ *
+ * Portions Copyright (c) 1996-$year, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * NOTES
+ * ******************************
+ * *** DO NOT EDIT THIS FILE! ***
+ * ******************************
+ *
+ * It has been generated by src/backend/utils/gencommandtag.pl
+ *
+ * IDENTIFICATION
+ * $repo_path
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef $header_guard
+#define $header_guard";
+}
+
+sub boilerplate_footer
+{
+ my ($header_guard) = @_;
+ return "#endif /* $header_guard */";
+}
+
+sub dataerror
+{
+ my ($errmsg, $errhint) = @_;
+ print STDERR "Error encountered while verifying $inputfile:\n";
+ print STDERR $errmsg, "\n";
+ if (defined $errhint)
+ {
+ print STDERR "HINT: ", $errhint, "\n";
+ }
+ print STDERR "Please fix $inputfile\n";
+ exit 1;
+}
+
+sub usage
+{
+ die <<EOM;
+Usage: perl gencommandtag.pl [--inputfile <file.dat>] [--outputdir <path>]
+
+Options:
+ --inputfile Optional: Input commandtag data file
+ (defaults to ../../include/utils/commandtag.dat)
+ --outputdir Optional: Output directory for generated headers
+ (defaults to current working directory)
+
+gencommandtag.pl generates the commandtag_enum.h and commandtag_behavior.h
+files from the specially formatted commandtag.dat data file.
+
+Report bugs to <pgsql-bugs\@lists.postgresql.org>.
+EOM
+}
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index b675575c31..00e8065342 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -281,7 +281,7 @@ void
PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan)
{
@@ -289,10 +289,12 @@ PortalDefineQuery(Portal portal,
AssertState(portal->status == PORTAL_NEW);
AssertArg(sourceText != NULL);
- AssertArg(commandTag != NULL || stmts == NIL);
+ AssertArg(commandTag != COMMANDTAG_UNKNOWN || stmts == NIL);
portal->prepStmtName = prepStmtName;
portal->sourceText = sourceText;
+ portal->qc.commandTag = commandTag;
+ portal->qc.nprocessed = 0;
portal->commandTag = commandTag;
portal->stmts = stmts;
portal->cplan = cplan;
diff --git a/src/include/Makefile b/src/include/Makefile
index c557375ae3..fadbd737c9 100644
--- a/src/include/Makefile
+++ b/src/include/Makefile
@@ -44,6 +44,8 @@ install: all installdirs
$(INSTALL_DATA) pg_config.h '$(DESTDIR)$(includedir_server)'
$(INSTALL_DATA) pg_config_ext.h '$(DESTDIR)$(includedir_server)'
$(INSTALL_DATA) pg_config_os.h '$(DESTDIR)$(includedir_server)'
+ $(INSTALL_DATA) utils/commandtag_enum.h '$(DESTDIR)$(includedir_server)/utils'
+ $(INSTALL_DATA) utils/commandtag_behavior.h '$(DESTDIR)$(includedir_server)/utils'
$(INSTALL_DATA) utils/errcodes.h '$(DESTDIR)$(includedir_server)/utils'
$(INSTALL_DATA) utils/fmgroids.h '$(DESTDIR)$(includedir_server)/utils'
$(INSTALL_DATA) utils/fmgrprotos.h '$(DESTDIR)$(includedir_server)/utils'
@@ -77,7 +79,7 @@ uninstall:
clean:
- rm -f utils/fmgroids.h utils/fmgrprotos.h utils/errcodes.h utils/header-stamp
+ rm -f utils/commandtag_enum.h utils/commandtag_behavior.h utils/fmgroids.h utils/fmgrprotos.h utils/errcodes.h utils/header-stamp
rm -f parser/gram.h storage/lwlocknames.h utils/probes.h
rm -f catalog/schemapg.h catalog/pg_*_d.h catalog/header-stamp
diff --git a/src/include/commands/createas.h b/src/include/commands/createas.h
index 7743851a38..5615b5ecac 100644
--- a/src/include/commands/createas.h
+++ b/src/include/commands/createas.h
@@ -22,7 +22,8 @@
extern ObjectAddress ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
- ParamListInfo params, QueryEnvironment *queryEnv, char *completionTag);
+ ParamListInfo params, QueryEnvironment *queryEnv,
+ QueryCompletion *qc);
extern int GetIntoRelEFlags(IntoClause *intoClause);
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index faa2958b89..5123c4b850 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -25,7 +25,7 @@ typedef struct EventTriggerData
NodeTag type;
const char *event; /* event name */
Node *parsetree; /* parse tree */
- const char *tag; /* command tag */
+ CommandTag tag;
} EventTriggerData;
#define AT_REWRITE_ALTER_PERSISTENCE 0x01
diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h
index 6bdb7ca258..3ea4f5c80b 100644
--- a/src/include/commands/matview.h
+++ b/src/include/commands/matview.h
@@ -24,7 +24,7 @@
extern void SetMatViewPopulatedState(Relation relation, bool newstate);
extern ObjectAddress ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag);
+ ParamListInfo params, QueryCompletion *qc);
extern DestReceiver *CreateTransientRelDestReceiver(Oid oid);
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index 4ecc1a2ecd..41bb7c395b 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -22,8 +22,7 @@
extern void PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo params,
bool isTopLevel);
-extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
- char *completionTag);
+extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest, QueryCompletion *qc);
extern void PerformPortalClose(const char *name);
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index a0509e1f33..4fcf2406c1 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -40,7 +40,7 @@ extern void PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
extern void ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void DeallocateQuery(DeallocateStmt *stmt);
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into,
ExplainState *es, const char *queryString,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index da0706add5..2c3ae6b603 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -27,6 +27,7 @@
#include "nodes/primnodes.h"
#include "nodes/value.h"
#include "partitioning/partdefs.h"
+#include "utils/commandtag.h"
typedef enum OverridingKind
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 35bce731a1..dd699b0993 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -68,7 +68,7 @@
#define DEST_H
#include "executor/tuptable.h"
-
+#include "utils/commandtag.h"
/* buffer size to use for command completion tags */
#define COMPLETION_TAG_BUFSIZE 64
@@ -134,9 +134,10 @@ extern PGDLLIMPORT DestReceiver *None_Receiver; /* permanent receiver for
/* The primary destination management functions */
-extern void BeginCommand(const char *commandTag, CommandDest dest);
+extern void BeginCommand(CommandTag commandTag, CommandDest dest);
extern DestReceiver *CreateDestReceiver(CommandDest dest);
-extern void EndCommand(const char *commandTag, CommandDest dest);
+extern void EndCommand(const QueryCompletion *qc, CommandDest dest,
+ bool force_undecorated_output);
/* Additional functions that go with destination management, more or less. */
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index 4ad6324e2d..437642cc72 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.h
@@ -35,7 +35,7 @@ extern void PortalSetResultFormat(Portal portal, int nFormats,
extern bool PortalRun(Portal portal, long count, bool isTopLevel,
bool run_once, DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletion *qc);
extern uint64 PortalRunFetch(Portal portal,
FetchDirection fdirection,
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index a551e08cb8..8781ac7d38 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -15,6 +15,7 @@
#define UTILITY_H
#include "tcop/tcopprot.h"
+#include "utils/commandtag.h"
typedef enum
{
@@ -71,17 +72,17 @@ typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
const char *queryString, ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void ProcessUtilityForAlterTable(Node *stmt,
AlterTableUtilityContext *context);
@@ -92,7 +93,13 @@ extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
extern Query *UtilityContainsQuery(Node *parsetree);
-extern const char *CreateCommandTag(Node *parsetree);
+extern CommandTag CreateCommandTag(Node *parsetree);
+
+static inline const char *
+CreateCommandName(Node *parsetree)
+{
+ return GetCommandTagName(CreateCommandTag(parsetree));
+}
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
diff --git a/src/include/utils/.gitignore b/src/include/utils/.gitignore
index 05cfa7a8d6..9bc600aacc 100644
--- a/src/include/utils/.gitignore
+++ b/src/include/utils/.gitignore
@@ -1,3 +1,5 @@
+/commandtag_behavior.h
+/commandtag_enum.h
/fmgroids.h
/fmgrprotos.h
/probes.h
diff --git a/src/include/utils/commandtag.dat b/src/include/utils/commandtag.dat
new file mode 100644
index 0000000000..a79ee50e00
--- /dev/null
+++ b/src/include/utils/commandtag.dat
@@ -0,0 +1,938 @@
+#----------------------------------------------------------------------
+#
+# commandtag.dat
+# Data file from which commandtag_enum.h and commandtag_behavior.h
+# are generated
+#
+# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/backend/utils/commandtag.dat
+#
+#----------------------------------------------------------------------
+
+# gencommandtag.pl will sort these records, so there is no requirement
+# that they be correctly sorted here, but by convention, we keep these
+# in sorted order by tagname.
+#
+# The 'taglabel' field is typically just a repeat of the tagname but
+# with spaces converted to underscores. It serves as the label within
+# the generated CommandTag enum. For weirder names, like ???, the
+# label does not match, for obvious reasons.
+#
+# The following boolean fields specify whether event trigger support
+# exists for the given command tag:
+#
+# event_trigger_ok
+# table_rewrite_ok
+#
+# And these two boolean fields specify the formatting that EndCommand
+# should send back to the client for the command tag:
+#
+# display_rowcount
+# display_last_oid
+#
+# For these four boolean fields, the default is false. Only specify
+# a value for records where they are true.
+[
+{
+ taglabel => 'UNKNOWN',
+ tagname => '???',
+},
+{
+ taglabel => 'ALTER_ACCESS_METHOD',
+ tagname => 'ALTER ACCESS METHOD',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_AGGREGATE',
+ tagname => 'ALTER AGGREGATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_CAST',
+ tagname => 'ALTER CAST',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_COLLATION',
+ tagname => 'ALTER COLLATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_CONSTRAINT',
+ tagname => 'ALTER CONSTRAINT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_CONVERSION',
+ tagname => 'ALTER CONVERSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_DATABASE',
+ tagname => 'ALTER DATABASE',
+},
+{
+ taglabel => 'ALTER_DEFAULT_PRIVILEGES',
+ tagname => 'ALTER DEFAULT PRIVILEGES',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_DOMAIN',
+ tagname => 'ALTER DOMAIN',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_EVENT_TRIGGER',
+ tagname => 'ALTER EVENT TRIGGER',
+},
+{
+ taglabel => 'ALTER_EXTENSION',
+ tagname => 'ALTER EXTENSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_FOREIGN_DATA_WRAPPER',
+ tagname => 'ALTER FOREIGN DATA WRAPPER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_FOREIGN_TABLE',
+ tagname => 'ALTER FOREIGN TABLE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_FUNCTION',
+ tagname => 'ALTER FUNCTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_INDEX',
+ tagname => 'ALTER INDEX',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_LANGUAGE',
+ tagname => 'ALTER LANGUAGE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_LARGE_OBJECT',
+ tagname => 'ALTER LARGE OBJECT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_MATERIALIZED_VIEW',
+ tagname => 'ALTER MATERIALIZED VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_OPERATOR',
+ tagname => 'ALTER OPERATOR',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_OPERATOR_CLASS',
+ tagname => 'ALTER OPERATOR CLASS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_OPERATOR_FAMILY',
+ tagname => 'ALTER OPERATOR FAMILY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_POLICY',
+ tagname => 'ALTER POLICY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_PROCEDURE',
+ tagname => 'ALTER PROCEDURE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_PUBLICATION',
+ tagname => 'ALTER PUBLICATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_ROLE',
+ tagname => 'ALTER ROLE',
+},
+{
+ taglabel => 'ALTER_ROUTINE',
+ tagname => 'ALTER ROUTINE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_RULE',
+ tagname => 'ALTER RULE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_SCHEMA',
+ tagname => 'ALTER SCHEMA',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_SEQUENCE',
+ tagname => 'ALTER SEQUENCE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_SERVER',
+ tagname => 'ALTER SERVER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_STATISTICS',
+ tagname => 'ALTER STATISTICS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_SUBSCRIPTION',
+ tagname => 'ALTER SUBSCRIPTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_SYSTEM',
+ tagname => 'ALTER SYSTEM',
+},
+{
+ taglabel => 'ALTER_TABLE',
+ tagname => 'ALTER TABLE',
+ event_trigger_ok => 'true',
+ table_rewrite_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TABLESPACE',
+ tagname => 'ALTER TABLESPACE',
+},
+{
+ taglabel => 'ALTER_TEXT_SEARCH_CONFIGURATION',
+ tagname => 'ALTER TEXT SEARCH CONFIGURATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TEXT_SEARCH_DICTIONARY',
+ tagname => 'ALTER TEXT SEARCH DICTIONARY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TEXT_SEARCH_PARSER',
+ tagname => 'ALTER TEXT SEARCH PARSER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TEXT_SEARCH_TEMPLATE',
+ tagname => 'ALTER TEXT SEARCH TEMPLATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TRANSFORM',
+ tagname => 'ALTER TRANSFORM',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TRIGGER',
+ tagname => 'ALTER TRIGGER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_TYPE',
+ tagname => 'ALTER TYPE',
+ event_trigger_ok => 'true',
+ table_rewrite_ok => 'true',
+},
+{
+ taglabel => 'ALTER_USER_MAPPING',
+ tagname => 'ALTER USER MAPPING',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ALTER_VIEW',
+ tagname => 'ALTER VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'ANALYZE',
+ tagname => 'ANALYZE',
+},
+{
+ taglabel => 'BEGIN',
+ tagname => 'BEGIN',
+},
+{
+ taglabel => 'CALL',
+ tagname => 'CALL',
+},
+{
+ taglabel => 'CHECKPOINT',
+ tagname => 'CHECKPOINT',
+},
+{
+ taglabel => 'CLOSE',
+ tagname => 'CLOSE',
+},
+{
+ taglabel => 'CLOSE_CURSOR',
+ tagname => 'CLOSE CURSOR',
+},
+{
+ taglabel => 'CLOSE_CURSOR_ALL',
+ tagname => 'CLOSE CURSOR ALL',
+},
+{
+ taglabel => 'CLUSTER',
+ tagname => 'CLUSTER',
+},
+{
+ taglabel => 'COMMENT',
+ tagname => 'COMMENT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'COMMIT',
+ tagname => 'COMMIT',
+},
+{
+ taglabel => 'COMMIT_PREPARED',
+ tagname => 'COMMIT PREPARED',
+},
+{
+ taglabel => 'COPY',
+ tagname => 'COPY',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'COPY_FROM',
+ tagname => 'COPY FROM',
+},
+{
+ taglabel => 'CREATE_ACCESS_METHOD',
+ tagname => 'CREATE ACCESS METHOD',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_AGGREGATE',
+ tagname => 'CREATE AGGREGATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_CAST',
+ tagname => 'CREATE CAST',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_COLLATION',
+ tagname => 'CREATE COLLATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_CONSTRAINT',
+ tagname => 'CREATE CONSTRAINT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_CONVERSION',
+ tagname => 'CREATE CONVERSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_DATABASE',
+ tagname => 'CREATE DATABASE',
+},
+{
+ taglabel => 'CREATE_DOMAIN',
+ tagname => 'CREATE DOMAIN',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_EVENT_TRIGGER',
+ tagname => 'CREATE EVENT TRIGGER',
+},
+{
+ taglabel => 'CREATE_EXTENSION',
+ tagname => 'CREATE EXTENSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_FOREIGN_DATA_WRAPPER',
+ tagname => 'CREATE FOREIGN DATA WRAPPER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_FOREIGN_TABLE',
+ tagname => 'CREATE FOREIGN TABLE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_FUNCTION',
+ tagname => 'CREATE FUNCTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_INDEX',
+ tagname => 'CREATE INDEX',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_LANGUAGE',
+ tagname => 'CREATE LANGUAGE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_MATERIALIZED_VIEW',
+ tagname => 'CREATE MATERIALIZED VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_OPERATOR',
+ tagname => 'CREATE OPERATOR',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_OPERATOR_CLASS',
+ tagname => 'CREATE OPERATOR CLASS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_OPERATOR_FAMILY',
+ tagname => 'CREATE OPERATOR FAMILY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_POLICY',
+ tagname => 'CREATE POLICY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_PROCEDURE',
+ tagname => 'CREATE PROCEDURE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_PUBLICATION',
+ tagname => 'CREATE PUBLICATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_ROLE',
+ tagname => 'CREATE ROLE',
+},
+{
+ taglabel => 'CREATE_ROUTINE',
+ tagname => 'CREATE ROUTINE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_RULE',
+ tagname => 'CREATE RULE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_SCHEMA',
+ tagname => 'CREATE SCHEMA',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_SEQUENCE',
+ tagname => 'CREATE SEQUENCE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_SERVER',
+ tagname => 'CREATE SERVER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_STATISTICS',
+ tagname => 'CREATE STATISTICS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_SUBSCRIPTION',
+ tagname => 'CREATE SUBSCRIPTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TABLE',
+ tagname => 'CREATE TABLE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TABLE_AS',
+ tagname => 'CREATE TABLE AS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TABLESPACE',
+ tagname => 'CREATE TABLESPACE',
+},
+{
+ taglabel => 'CREATE_TEXT_SEARCH_CONFIGURATION',
+ tagname => 'CREATE TEXT SEARCH CONFIGURATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TEXT_SEARCH_DICTIONARY',
+ tagname => 'CREATE TEXT SEARCH DICTIONARY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TEXT_SEARCH_PARSER',
+ tagname => 'CREATE TEXT SEARCH PARSER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TEXT_SEARCH_TEMPLATE',
+ tagname => 'CREATE TEXT SEARCH TEMPLATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TRANSFORM',
+ tagname => 'CREATE TRANSFORM',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TRIGGER',
+ tagname => 'CREATE TRIGGER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_TYPE',
+ tagname => 'CREATE TYPE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_USER_MAPPING',
+ tagname => 'CREATE USER MAPPING',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'CREATE_VIEW',
+ tagname => 'CREATE VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DEALLOCATE',
+ tagname => 'DEALLOCATE',
+},
+{
+ taglabel => 'DEALLOCATE_ALL',
+ tagname => 'DEALLOCATE ALL',
+},
+{
+ taglabel => 'DECLARE_CURSOR',
+ tagname => 'DECLARE CURSOR',
+},
+{
+ taglabel => 'DELETE',
+ tagname => 'DELETE',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'DISCARD',
+ tagname => 'DISCARD',
+},
+{
+ taglabel => 'DISCARD_ALL',
+ tagname => 'DISCARD ALL',
+},
+{
+ taglabel => 'DISCARD_PLANS',
+ tagname => 'DISCARD PLANS',
+},
+{
+ taglabel => 'DISCARD_SEQUENCES',
+ tagname => 'DISCARD SEQUENCES',
+},
+{
+ taglabel => 'DISCARD_TEMP',
+ tagname => 'DISCARD TEMP',
+},
+{
+ taglabel => 'DO',
+ tagname => 'DO',
+},
+{
+ taglabel => 'DROP_ACCESS_METHOD',
+ tagname => 'DROP ACCESS METHOD',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_AGGREGATE',
+ tagname => 'DROP AGGREGATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_CAST',
+ tagname => 'DROP CAST',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_COLLATION',
+ tagname => 'DROP COLLATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_CONSTRAINT',
+ tagname => 'DROP CONSTRAINT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_CONVERSION',
+ tagname => 'DROP CONVERSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_DATABASE',
+ tagname => 'DROP DATABASE',
+},
+{
+ taglabel => 'DROP_DOMAIN',
+ tagname => 'DROP DOMAIN',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_EVENT_TRIGGER',
+ tagname => 'DROP EVENT TRIGGER',
+},
+{
+ taglabel => 'DROP_EXTENSION',
+ tagname => 'DROP EXTENSION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_FOREIGN_DATA_WRAPPER',
+ tagname => 'DROP FOREIGN DATA WRAPPER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_FOREIGN_TABLE',
+ tagname => 'DROP FOREIGN TABLE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_FUNCTION',
+ tagname => 'DROP FUNCTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_INDEX',
+ tagname => 'DROP INDEX',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_LANGUAGE',
+ tagname => 'DROP LANGUAGE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_MATERIALIZED_VIEW',
+ tagname => 'DROP MATERIALIZED VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_OPERATOR',
+ tagname => 'DROP OPERATOR',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_OPERATOR_CLASS',
+ tagname => 'DROP OPERATOR CLASS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_OPERATOR_FAMILY',
+ tagname => 'DROP OPERATOR FAMILY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_OWNED',
+ tagname => 'DROP OWNED',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_POLICY',
+ tagname => 'DROP POLICY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_PROCEDURE',
+ tagname => 'DROP PROCEDURE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_PUBLICATION',
+ tagname => 'DROP PUBLICATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_REPLICATION_SLOT',
+ tagname => 'DROP REPLICATION SLOT',
+},
+{
+ taglabel => 'DROP_ROLE',
+ tagname => 'DROP ROLE',
+},
+{
+ taglabel => 'DROP_ROUTINE',
+ tagname => 'DROP ROUTINE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_RULE',
+ tagname => 'DROP RULE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_SCHEMA',
+ tagname => 'DROP SCHEMA',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_SEQUENCE',
+ tagname => 'DROP SEQUENCE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_SERVER',
+ tagname => 'DROP SERVER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_STATISTICS',
+ tagname => 'DROP STATISTICS',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_SUBSCRIPTION',
+ tagname => 'DROP SUBSCRIPTION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TABLE',
+ tagname => 'DROP TABLE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TABLESPACE',
+ tagname => 'DROP TABLESPACE',
+},
+{
+ taglabel => 'DROP_TEXT_SEARCH_CONFIGURATION',
+ tagname => 'DROP TEXT SEARCH CONFIGURATION',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TEXT_SEARCH_DICTIONARY',
+ tagname => 'DROP TEXT SEARCH DICTIONARY',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TEXT_SEARCH_PARSER',
+ tagname => 'DROP TEXT SEARCH PARSER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TEXT_SEARCH_TEMPLATE',
+ tagname => 'DROP TEXT SEARCH TEMPLATE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TRANSFORM',
+ tagname => 'DROP TRANSFORM',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TRIGGER',
+ tagname => 'DROP TRIGGER',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_TYPE',
+ tagname => 'DROP TYPE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_USER_MAPPING',
+ tagname => 'DROP USER MAPPING',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'DROP_VIEW',
+ tagname => 'DROP VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'EXECUTE',
+ tagname => 'EXECUTE',
+},
+{
+ taglabel => 'EXPLAIN',
+ tagname => 'EXPLAIN',
+},
+{
+ taglabel => 'FETCH',
+ tagname => 'FETCH',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'GRANT',
+ tagname => 'GRANT',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'GRANT_ROLE',
+ tagname => 'GRANT ROLE',
+},
+{
+ taglabel => 'IMPORT_FOREIGN_SCHEMA',
+ tagname => 'IMPORT FOREIGN SCHEMA',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'INSERT',
+ tagname => 'INSERT',
+ display_last_oid => 'true',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'LISTEN',
+ tagname => 'LISTEN',
+},
+{
+ taglabel => 'LOAD',
+ tagname => 'LOAD',
+},
+{
+ taglabel => 'LOCK_TABLE',
+ tagname => 'LOCK TABLE',
+},
+{
+ taglabel => 'MOVE',
+ tagname => 'MOVE',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'NOTIFY',
+ tagname => 'NOTIFY',
+},
+{
+ taglabel => 'PREPARE',
+ tagname => 'PREPARE',
+},
+{
+ taglabel => 'PREPARE_TRANSACTION',
+ tagname => 'PREPARE TRANSACTION',
+},
+{
+ taglabel => 'REASSIGN_OWNED',
+ tagname => 'REASSIGN OWNED',
+},
+{
+ taglabel => 'REFRESH_MATERIALIZED_VIEW',
+ tagname => 'REFRESH MATERIALIZED VIEW',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'REINDEX',
+ tagname => 'REINDEX',
+},
+{
+ taglabel => 'RELEASE',
+ tagname => 'RELEASE',
+},
+{
+ taglabel => 'RESET',
+ tagname => 'RESET',
+},
+{
+ taglabel => 'REVOKE',
+ tagname => 'REVOKE',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'REVOKE_ROLE',
+ tagname => 'REVOKE ROLE',
+},
+{
+ taglabel => 'ROLLBACK',
+ tagname => 'ROLLBACK',
+},
+{
+ taglabel => 'ROLLBACK_PREPARED',
+ tagname => 'ROLLBACK PREPARED',
+},
+{
+ taglabel => 'SAVEPOINT',
+ tagname => 'SAVEPOINT',
+},
+{
+ taglabel => 'SECURITY_LABEL',
+ tagname => 'SECURITY LABEL',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'SELECT',
+ tagname => 'SELECT',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'SELECT_FOR_KEY_SHARE',
+ tagname => 'SELECT FOR KEY SHARE',
+},
+{
+ taglabel => 'SELECT_FOR_NO_KEY_UPDATE',
+ tagname => 'SELECT FOR NO KEY UPDATE',
+},
+{
+ taglabel => 'SELECT_FOR_SHARE',
+ tagname => 'SELECT FOR SHARE',
+},
+{
+ taglabel => 'SELECT_FOR_UPDATE',
+ tagname => 'SELECT FOR UPDATE',
+},
+{
+ taglabel => 'SELECT_INTO',
+ tagname => 'SELECT INTO',
+ event_trigger_ok => 'true',
+},
+{
+ taglabel => 'SET',
+ tagname => 'SET',
+},
+{
+ taglabel => 'SET_CONSTRAINTS',
+ tagname => 'SET CONSTRAINTS',
+},
+{
+ taglabel => 'SHOW',
+ tagname => 'SHOW',
+},
+{
+ taglabel => 'START_TRANSACTION',
+ tagname => 'START TRANSACTION',
+},
+{
+ taglabel => 'TRUNCATE_TABLE',
+ tagname => 'TRUNCATE TABLE',
+},
+{
+ taglabel => 'UNLISTEN',
+ tagname => 'UNLISTEN',
+},
+{
+ taglabel => 'UPDATE',
+ tagname => 'UPDATE',
+ display_rowcount => 'true',
+},
+{
+ taglabel => 'VACUUM',
+ tagname => 'VACUUM',
+},
+]
diff --git a/src/include/utils/commandtag.h b/src/include/utils/commandtag.h
new file mode 100644
index 0000000000..ea9b2c682a
--- /dev/null
+++ b/src/include/utils/commandtag.h
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * commandtag.h
+ * Declarations for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/commandtag.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMMANDTAG_H
+#define COMMANDTAG_H
+
+#include "utils/commandtag_enum.h"
+
+typedef struct CommandTagBehavior {
+ const char *name;
+ const bool display_last_oid;
+ const bool display_rowcount;
+ const bool event_trigger_ok;
+ const bool table_rewrite_ok;
+} CommandTagBehavior;
+
+extern const CommandTagBehavior tag_behavior[];
+
+typedef struct QueryCompletion
+{
+ CommandTag commandTag;
+ uint64 nprocessed;
+} QueryCompletion;
+
+static inline void
+SetQueryCompletion(QueryCompletion *qc, CommandTag commandTag, uint64 nprocessed)
+{
+ qc->commandTag = commandTag;
+ qc->nprocessed = nprocessed;
+}
+
+static inline void
+CopyQueryCompletion(QueryCompletion *dst, const QueryCompletion *src)
+{
+ dst->commandTag = src->commandTag;
+ dst->nprocessed = src->nprocessed;
+}
+
+extern void InitializeQueryCompletion(QueryCompletion *qc);
+extern const char *GetCommandTagName(CommandTag commandTag);
+extern bool command_tag_display_last_oid(CommandTag commandTag);
+extern bool command_tag_display_rowcount(CommandTag commandTag);
+extern bool command_tag_event_trigger_ok(CommandTag commandTag);
+extern bool command_tag_table_rewrite_ok(CommandTag commandTag);
+extern CommandTag GetCommandTagEnum(const char *tagname);
+extern void PreventCommandTagIfReadOnly(CommandTag commandTag);
+extern void PreventCommandTagIfParallelMode(CommandTag commandTag);
+extern void PreventCommandTagDuringRecovery(CommandTag commandTag);
+
+#endif /* COMMANDTAG_H */
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index 6c3ff81ba3..bc8ce48061 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -28,8 +28,7 @@ typedef struct
{
Oid fnoid; /* function to be called */
char enabled; /* as SESSION_REPLICATION_ROLE_* */
- int ntags; /* number of command tags */
- char **tag; /* command tags in SORTED order */
+ Bitmapset *tagset; /* command tags, or NULL if empty */
} EventTriggerCacheItem;
extern List *EventCacheLookup(EventTriggerEvent event);
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index e48661ebec..b2900307ad 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -18,6 +18,7 @@
#include "access/tupdesc.h"
#include "lib/ilist.h"
#include "nodes/params.h"
+#include "utils/commandtag.h"
#include "utils/queryenvironment.h"
/* Forward declaration, to avoid including parsenodes.h here */
@@ -95,7 +96,7 @@ typedef struct CachedPlanSource
int magic; /* should equal CACHEDPLANSOURCE_MAGIC */
struct RawStmt *raw_parse_tree; /* output of raw_parser(), or NULL */
const char *query_string; /* source text of query */
- const char *commandTag; /* command tag (a constant!), or NULL */
+ CommandTag commandTag;
Oid *param_types; /* array of parameter type OIDs, or NULL */
int num_params; /* length of param_types array */
ParserSetupHook parserSetup; /* alternative parameter spec method */
@@ -186,10 +187,10 @@ extern void ResetPlanCache(void);
extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern void CompleteCachedPlan(CachedPlanSource *plansource,
List *querytree_list,
MemoryContext querytree_context,
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 0b69433722..b398ae45a3 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -48,6 +48,7 @@
#include "datatype/timestamp.h"
#include "executor/execdesc.h"
+#include "utils/commandtag.h"
#include "utils/plancache.h"
#include "utils/resowner.h"
@@ -132,7 +133,8 @@ typedef struct PortalData
/* The query or queries the portal will execute */
const char *sourceText; /* text of query (as of 8.4, never NULL) */
- const char *commandTag; /* command tag for original query */
+ CommandTag commandTag; /* command tag for original query */
+ QueryCompletion qc; /* command completion data for executed query */
List *stmts; /* list of PlannedStmts */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
@@ -227,7 +229,7 @@ extern Portal GetPortalByName(const char *name);
extern void PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan);
extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 5acf604f63..e165790d66 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -41,6 +41,7 @@
#include "tcop/utility.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/commandtag.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
@@ -1473,7 +1474,7 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
case PLPGSQL_PROMISE_TG_TAG:
if (estate->evtrigdata == NULL)
elog(ERROR, "event trigger promise is not in an event trigger function");
- assign_text_var(estate, var, estate->evtrigdata->tag);
+ assign_text_var(estate, var, GetCommandTagName(estate->evtrigdata->tag));
break;
default:
@@ -4115,10 +4116,9 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
* tree(s), since those are the result of rewriting and could have
* been transmogrified into something else entirely.
*/
- if (plansource->commandTag &&
- (strcmp(plansource->commandTag, "INSERT") == 0 ||
- strcmp(plansource->commandTag, "UPDATE") == 0 ||
- strcmp(plansource->commandTag, "DELETE") == 0))
+ if (plansource->commandTag == COMMANDTAG_INSERT ||
+ plansource->commandTag == COMMANDTAG_UPDATE ||
+ plansource->commandTag == COMMANDTAG_DELETE)
{
stmt->mod_stmt = true;
break;
diff --git a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
index e1629ec618..b7bdb88ce7 100644
--- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
+++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
@@ -74,7 +74,7 @@ get_command_tag(PG_FUNCTION_ARGS)
if (!cmd->parsetree)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(cstring_to_text(CreateCommandTag(cmd->parsetree)));
+ PG_RETURN_TEXT_P(cstring_to_text(CreateCommandName(cmd->parsetree)));
}
/*
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 8412ef298e..5afc83498f 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -558,6 +558,7 @@ sub GenerateFiles
chdir('src/backend/utils');
my $pg_proc_dat = '../../../src/include/catalog/pg_proc.dat';
+ my $commandtag_dat = '../../../src/include/utils/commandtag.dat';
if ( IsNewer('fmgr-stamp', 'Gen_fmgrtab.pl')
|| IsNewer('fmgr-stamp', '../catalog/Catalog.pm')
|| IsNewer('fmgr-stamp', $pg_proc_dat)
@@ -570,6 +571,17 @@ sub GenerateFiles
|| confess "Could not touch fmgr-stamp";
close($f);
}
+ if ( IsNewer('commandtag-stamp', 'gencommandtag.pl')
+ || IsNewer('commandtag-stamp', 'DataFile.pm')
+ || IsNewer('commandtag-stamp', $commandtag_dat))
+ {
+ system(
+ "perl -I ../../../src/include/utils gencommandtag.pl --headerdir=../../../src/include/utils --sourcedir=. --inputfile=$commandtag_dat"
+ );
+ open(my $f, '>', 'commandtag-stamp')
+ || confess "Could not touch commandtag-stamp";
+ close($f);
+ }
chdir('../../..');
if (IsNewer(
--
2.21.1 (Apple Git-122.3)
On Thu, Feb 20, 2020 at 5:16 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
On Feb 19, 2020, at 3:31 AM, John Naylor <john.naylor@2ndquadrant.com> wrote:
thought it through. For one advantage, I think it might be nicer to
have indexing.dat and toasting.dat instead of having to dig the info
out of C macros. This was evident while recently experimenting with
generating catcache control data.I guess you mean macros DECLARE_UNIQUE_INDEX and DECLARE_TOAST. I don’t mind converting that to .dat files, though I’m mindful of Tom’s concern expressed early in this thread about the amount of code churn. Is there sufficient demand for refactoring this stuff? There are more reasons in the conversation below to refactor the perl modules and code generation scripts.
Yes, I was referring to those macros, but I did not mean to imply you
should convert them, either now or ever. I was just thinking out loud
about the possibility. :-)
That said, if we ever want Catalog.pm to delegate vivification to
DataFile.pm, there will eventually need to be a way to optionally
preserve comments and whitespace.
I have taken all this advice in v5 of the patch. --inputfile and --outputdir (previously named --sourcedir) are now optional with the defaults as you suggested.
+my $inputfile = '../../include/utils/commandtag.dat';
I think we should skip the default for the input file, since the
relative path is fragile and every such script I've seen requires it
to be passed in.
DataFiles.pm
[...]
I'm not familiar with using different IO routines depending on the OS
-- what's the benefit of that?I think you are talking about the slurp_file routine. That came directly from the TestLib.pm module. I don't have enough perl-on-windows experience to comment about why it does things that way.
Yes, that one, sorry I wasn't explicit.
Should I submit a separate patch refactoring the location of perl modules and functions of general interest into src/tools? src/tools/perl?
We may have accumulated enough disparate functionality by now to
consider this, but it sounds like PG14 material in any case.
I expect there will have to be a v6 once this has been adequately debated.
So far I haven't heard any justification for why it should remain in
src/backend/catalog, when it has nothing to do with catalogs. We don't
have to have a standard other-place for there to be a better
other-place.
--
gencommandtag.pl
sanity_check_data() seems longer than the main body of the script
(minus header boilerplate), and I wonder if we can pare it down some.
For one, I have difficulty imagining anyone would accidentally type an
unprintable or non-ascii character in a command tag and somehow not
realize it.
[...]
For another, duplicating checks that were done earlier
seems like a maintenance headache.[ detailed rebuttals about the above points ]
Those were just examples that stuck out at me, so even if I were
convinced to your side on those, my broader point was: the sanity
check seems way over-engineered for something that spits out an enum
and an array. Maybe I'm not imaginative enough. I found it hard to
read in any case.
Catalog.pm writes a temporary file and then moves it to the final file name at the end. DataFile buffers the output and only writes it after all the code generation has succeeded. There is no principled basis for these two modules tackling the same problem in two different ways.
Not the same problem. The temp files were originally for parallel Make
hazards, and now to prevent large portions of the backend to be
rebuilt. I actually think partially written files can be helpful for
debugging, so I don't even think it's a problem. But as I said, it
doesn't matter terribly much either way.
Speaking of boilerplate, it's better for readability to separate that
from actual code such as:typedef enum CommandTag
{
#define FIRST_COMMANDTAG COMMANDTAG_$sorted[0]->{taglabel})Good idea. While I was doing this, I also consolidated the duplicated boilerplate into just one function. I think this function, too, should go in just one perl module somewhere. See boilerplate_header() for details.
I like this a lot.
While thinking, I wonder if it makes sense to have a CodeGen module,
which would contain e.g. the new ParseData function,
FindDefinedSymbol, and functions for writing boilerplate.
--
John Naylor https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Thinking about this some more, would it be possible to treat these
like we do parser/kwlist.h? Something like this:
commandtag_list.h:
PG_COMMANDTAG(ALTER_ACCESS_METHOD, "ALTER ACCESS METHOD", true, false,
false, false)
...
then, just:
#define PG_COMMANDTAG(taglabel, tagname, event_trigger, table_rewrite,
display_rowcount, display_oid) label,
typedef enum CommandTag
{
#include "commandtag_list.h"
}
#undef PG_COMMANDTAG
...and then:
#define PG_COMMANDTAG(taglabel, tagname, event_trigger, table_rewrite,
display_rowcount, display_oid) \
{ tagname, event_trigger, table_rewrite, display_rowcount, display_oid },
const CommandTagBehavior tag_behavior[] =
{
#include "commandtag_list.h"
}
#undef PG_COMMANDTAG
I'm hand-waving a bit, and it doesn't have the flexibility of a .dat
file, but it's a whole lot simpler.
--
John Naylor https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 2020-Feb-21, John Naylor wrote:
Thinking about this some more, would it be possible to treat these
like we do parser/kwlist.h? Something like this:commandtag_list.h:
PG_COMMANDTAG(ALTER_ACCESS_METHOD, "ALTER ACCESS METHOD", true, false,
false, false)
...
I liked this idea, so I'm halfway on it now.
I'm hand-waving a bit, and it doesn't have the flexibility of a .dat
file, but it's a whole lot simpler.
Yeah, I for one don't want to maintain the proposed DataFile.pm.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 2020-Feb-28, Alvaro Herrera wrote:
On 2020-Feb-21, John Naylor wrote:
Thinking about this some more, would it be possible to treat these
like we do parser/kwlist.h? Something like this:commandtag_list.h:
PG_COMMANDTAG(ALTER_ACCESS_METHOD, "ALTER ACCESS METHOD", true, false,
false, false)
...I liked this idea, so I'm halfway on it now.
Here.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
v6-0001-Migrating-commandTag-from-string-to-enum.patchtext/x-diff; charset=utf-8Download
From ff9a1de6f8d589281841dfde3a6e096f8f992f81 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Thu, 6 Feb 2020 11:41:44 -0800
Subject: [PATCH v6] Migrating commandTag from string to enum.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The backend was using strings to represent command tags and doing string
comparisons in multiple places. Fixing that by creating a new
CommandTag enum and using it instead.
Replacing numerous occurrences of char *completionTag with a
QueryCompletionData struct so that the code no longer stores information
about completed queries in a cstring. Only at the last moment, in
EndCommand(), does this get converted to a string.
EventTriggerCacheItem no longer holds an array of pallocâd tag strings
in sorted order, but rather just a Bitmapset over the CommandTags.
Author: Mark Dilger, Ãlvaro Herrera
---
.../pg_stat_statements/pg_stat_statements.c | 18 +-
contrib/sepgsql/hooks.c | 6 +-
doc/src/sgml/event-trigger.sgml | 2 +-
src/backend/commands/createas.c | 14 +-
src/backend/commands/event_trigger.c | 160 +----
src/backend/commands/matview.c | 2 +-
src/backend/commands/portalcmds.c | 16 +-
src/backend/commands/prepare.c | 4 +-
src/backend/executor/execMain.c | 4 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/spi.c | 22 +-
src/backend/replication/logical/decode.c | 2 +-
src/backend/replication/walsender.c | 18 +-
src/backend/tcop/Makefile | 1 +
src/backend/tcop/cmdtag.c | 105 ++++
src/backend/tcop/dest.c | 43 +-
src/backend/tcop/postgres.c | 24 +-
src/backend/tcop/pquery.c | 112 ++--
src/backend/tcop/utility.c | 560 +++++++++---------
src/backend/utils/cache/evtcache.c | 30 +-
src/backend/utils/cache/plancache.c | 4 +-
src/backend/utils/mmgr/portalmem.c | 6 +-
src/include/commands/createas.h | 3 +-
src/include/commands/event_trigger.h | 3 +-
src/include/commands/matview.h | 2 +-
src/include/commands/portalcmds.h | 2 +-
src/include/commands/prepare.h | 2 +-
src/include/tcop/cmdtag.h | 57 ++
src/include/tcop/cmdtaglist.h | 208 +++++++
src/include/tcop/dest.h | 6 +-
src/include/tcop/pquery.h | 2 +-
src/include/tcop/utility.h | 15 +-
src/include/utils/evtcache.h | 3 +-
src/include/utils/plancache.h | 7 +-
src/include/utils/portal.h | 6 +-
src/pl/plperl/plperl.c | 2 +-
src/pl/plpgsql/src/pl_exec.c | 10 +-
src/pl/tcl/pltcl.c | 3 +-
.../test_ddl_deparse/test_ddl_deparse.c | 2 +-
39 files changed, 882 insertions(+), 608 deletions(-)
create mode 100644 src/backend/tcop/cmdtag.c
create mode 100644 src/include/tcop/cmdtag.h
create mode 100644 src/include/tcop/cmdtaglist.h
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index e4fda4b404..7b8e690c95 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -307,7 +307,7 @@ static void pgss_ExecutorEnd(QueryDesc *queryDesc);
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
static uint64 pgss_hash_string(const char *str, int len);
static void pgss_store(const char *query, uint64 queryId,
int query_location, int query_len,
@@ -960,7 +960,7 @@ static void
pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context,
ParamListInfo params, QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
@@ -998,11 +998,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
@@ -1013,10 +1013,8 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
INSTR_TIME_SET_CURRENT(duration);
INSTR_TIME_SUBTRACT(duration, start);
- /* parse command tag to retrieve the number of affected rows. */
- if (completionTag &&
- strncmp(completionTag, "COPY ", 5) == 0)
- rows = pg_strtouint64(completionTag + 5, NULL, 10);
+ if (qc && qc->commandTag == CMDTAG_COPY)
+ rows = qc->nprocessed;
else
rows = 0;
@@ -1060,11 +1058,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
}
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 997a64c87e..853b5b04ab 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -317,7 +317,7 @@ sepgsql_utility_command(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
@@ -380,11 +380,11 @@ sepgsql_utility_command(PlannedStmt *pstmt,
if (next_ProcessUtility_hook)
(*next_ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index 18628c498b..130f6cd886 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -1074,7 +1074,7 @@ typedef struct EventTriggerData
NodeTag type;
const char *event; /* event name */
Node *parsetree; /* parse tree */
- const char *tag; /* command tag */
+ CommandTag tag; /* command tag */
} EventTriggerData;
</programlisting>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cc02cf824e..3a5676fb39 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -10,7 +10,7 @@
*
* Formerly, CTAS was implemented as a variant of SELECT, which led
* to assorted legacy behaviors that we still try to preserve, notably that
- * we must return a tuples-processed count in the completionTag. (We no
+ * we must return a tuples-processed count in the QueryCompletion. (We no
* longer do that for CTAS ... WITH NO DATA, however.)
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
@@ -225,7 +225,7 @@ create_ctas_nodata(List *tlist, IntoClause *into)
ObjectAddress
ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ParamListInfo params, QueryEnvironment *queryEnv,
- char *completionTag)
+ QueryCompletion *qc)
{
Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
@@ -270,7 +270,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
Assert(!is_matview); /* excluded by syntax */
- ExecuteQuery(pstate, estmt, into, params, dest, completionTag);
+ ExecuteQuery(pstate, estmt, into, params, dest, qc);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
@@ -352,11 +352,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* run the plan to completion */
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
- /* save the rowcount if we're given a completionTag to fill */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ /* save the rowcount if we're given a qc to fill */
+ if (qc)
+ SetQueryCompletion(qc, CMDTAG_SELECT, queryDesc->estate->es_processed);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 71911d4067..a366869369 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -78,59 +78,6 @@ typedef struct
bool supported;
} event_trigger_support_data;
-typedef enum
-{
- EVENT_TRIGGER_COMMAND_TAG_OK,
- EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED,
- EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED
-} event_trigger_command_tag_check_result;
-
-/* XXX merge this with ObjectTypeMap? */
-static const event_trigger_support_data event_trigger_support[] = {
- {"ACCESS METHOD", true},
- {"AGGREGATE", true},
- {"CAST", true},
- {"CONSTRAINT", true},
- {"COLLATION", true},
- {"CONVERSION", true},
- {"DATABASE", false},
- {"DOMAIN", true},
- {"EXTENSION", true},
- {"EVENT TRIGGER", false},
- {"FOREIGN DATA WRAPPER", true},
- {"FOREIGN TABLE", true},
- {"FUNCTION", true},
- {"INDEX", true},
- {"LANGUAGE", true},
- {"MATERIALIZED VIEW", true},
- {"OPERATOR", true},
- {"OPERATOR CLASS", true},
- {"OPERATOR FAMILY", true},
- {"POLICY", true},
- {"PROCEDURE", true},
- {"PUBLICATION", true},
- {"ROLE", false},
- {"ROUTINE", true},
- {"RULE", true},
- {"SCHEMA", true},
- {"SEQUENCE", true},
- {"SERVER", true},
- {"STATISTICS", true},
- {"SUBSCRIPTION", true},
- {"TABLE", true},
- {"TABLESPACE", false},
- {"TRANSFORM", true},
- {"TRIGGER", true},
- {"TEXT SEARCH CONFIGURATION", true},
- {"TEXT SEARCH DICTIONARY", true},
- {"TEXT SEARCH PARSER", true},
- {"TEXT SEARCH TEMPLATE", true},
- {"TYPE", true},
- {"USER MAPPING", true},
- {"VIEW", true},
- {NULL, false}
-};
-
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -150,8 +97,6 @@ typedef struct SQLDropObject
static void AlterEventTriggerOwner_internal(Relation rel,
HeapTuple tup,
Oid newOwnerId);
-static event_trigger_command_tag_check_result check_ddl_tag(const char *tag);
-static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(const char *tag);
static void error_duplicate_filter_variable(const char *defname);
static Datum filter_list_to_array(List *filterlist);
static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname,
@@ -259,71 +204,23 @@ validate_ddl_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
- event_trigger_command_tag_check_result result;
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
- result = check_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED)
+ if (commandTag == CMDTAG_UNKNOWN)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
- tag, filtervar)));
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
+ tagstr, filtervar)));
+ if (!command_tag_event_trigger_ok(commandTag))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
-static event_trigger_command_tag_check_result
-check_ddl_tag(const char *tag)
-{
- const char *obtypename;
- const event_trigger_support_data *etsd;
-
- /*
- * Handle some idiosyncratic special cases.
- */
- if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
- pg_strcasecmp(tag, "SELECT INTO") == 0 ||
- pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
- pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
- pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
- pg_strcasecmp(tag, "COMMENT") == 0 ||
- pg_strcasecmp(tag, "GRANT") == 0 ||
- pg_strcasecmp(tag, "REVOKE") == 0 ||
- pg_strcasecmp(tag, "DROP OWNED") == 0 ||
- pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
- /*
- * Otherwise, command should be CREATE, ALTER, or DROP.
- */
- if (pg_strncasecmp(tag, "CREATE ", 7) == 0)
- obtypename = tag + 7;
- else if (pg_strncasecmp(tag, "ALTER ", 6) == 0)
- obtypename = tag + 6;
- else if (pg_strncasecmp(tag, "DROP ", 5) == 0)
- obtypename = tag + 5;
- else
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
-
- /*
- * ...and the object type should be something recognizable.
- */
- for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++)
- if (pg_strcasecmp(etsd->obtypename, obtypename) == 0)
- break;
- if (etsd->obtypename == NULL)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
- if (!etsd->supported)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-}
-
/*
* Validate DDL command tags for event table_rewrite.
*/
@@ -334,29 +231,18 @@ validate_table_rewrite_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
- event_trigger_command_tag_check_result result;
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
- result = check_table_rewrite_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
+ if (!command_tag_table_rewrite_ok(commandTag))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
-static event_trigger_command_tag_check_result
-check_table_rewrite_ddl_tag(const char *tag)
-{
- if (pg_strcasecmp(tag, "ALTER TABLE") == 0 ||
- pg_strcasecmp(tag, "ALTER TYPE") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
-}
-
/*
* Complain about a duplicate filter variable.
*/
@@ -663,7 +549,7 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
* tags matching.
*/
static bool
-filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
+filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
{
/*
* Filter by session replication role, knowing that we never see disabled
@@ -681,9 +567,7 @@ filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
}
/* Filter by tags, if any were specified. */
- if (item->ntags != 0 && bsearch(tag, item->tag,
- item->ntags, sizeof(char *),
- pg_qsort_strcmp) == NULL)
+ if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
return false;
/* if we reach that point, we're not filtering out this item */
@@ -700,7 +584,7 @@ EventTriggerCommonSetup(Node *parsetree,
EventTriggerEvent event, const char *eventstr,
EventTriggerData *trigdata)
{
- const char *tag;
+ CommandTag tag;
List *cachelist;
ListCell *lc;
List *runlist = NIL;
@@ -716,25 +600,25 @@ EventTriggerCommonSetup(Node *parsetree,
*
* If this cross-check fails for you, you probably need to either adjust
* standard_ProcessUtility() not to invoke event triggers for the command
- * type in question, or you need to adjust check_ddl_tag to accept the
+ * type in question, or you need to adjust event_trigger_ok to accept the
* relevant command tag.
*/
#ifdef USE_ASSERT_CHECKING
{
- const char *dbgtag;
+ CommandTag dbgtag;
dbgtag = CreateCommandTag(parsetree);
if (event == EVT_DDLCommandStart ||
event == EVT_DDLCommandEnd ||
event == EVT_SQLDrop)
{
- if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ if (!command_tag_event_trigger_ok(dbgtag))
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
else if (event == EVT_TableRewrite)
{
- if (check_table_rewrite_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ if (!command_tag_table_rewrite_ok(dbgtag))
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
}
#endif
@@ -758,7 +642,7 @@ EventTriggerCommonSetup(Node *parsetree,
{
EventTriggerCacheItem *item = lfirst(lc);
- if (filter_event_trigger(&tag, item))
+ if (filter_event_trigger(tag, item))
{
/* We must plan to fire this trigger. */
runlist = lappend_oid(runlist, item->fnoid);
@@ -2136,7 +2020,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
values[i++] = Int32GetDatum(addr.objectSubId);
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
/* object_type */
values[i++] = CStringGetTextDatum(type);
/* schema */
@@ -2161,7 +2045,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
nulls[i++] = true;
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
/* object_type */
values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
/* schema */
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 1ee37c1aeb..c3954f3e24 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -136,7 +136,7 @@ SetMatViewPopulatedState(Relation relation, bool newstate)
*/
ObjectAddress
ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag)
+ ParamListInfo params, QueryCompletion *qc)
{
Oid matviewOid;
Relation matviewRel;
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 7e5c805a1e..40be5069fe 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -106,7 +106,7 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
PortalDefineQuery(portal,
NULL,
queryString,
- "SELECT", /* cursor's query is always a SELECT */
+ CMDTAG_SELECT, /* cursor's query is always a SELECT */
list_make1(plan),
NULL);
@@ -160,15 +160,14 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
*
* stmt: parsetree node for command
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store a command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*/
void
PerformPortalFetch(FetchStmt *stmt,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Portal portal;
uint64 nprocessed;
@@ -203,10 +202,9 @@ PerformPortalFetch(FetchStmt *stmt,
dest);
/* Return command status if wanted */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT,
- stmt->ismove ? "MOVE" : "FETCH",
- nprocessed);
+ if (qc)
+ SetQueryCompletion(qc, stmt->ismove ? CMDTAG_MOVE : CMDTAG_FETCH,
+ nprocessed);
}
/*
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index c4e4b6eaec..f917fc9c7a 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -187,7 +187,7 @@ void
ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
PreparedStatement *entry;
CachedPlan *cplan;
@@ -288,7 +288,7 @@ ExecuteQuery(ParseState *pstate,
*/
PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
- (void) PortalRun(portal, count, false, true, dest, dest, completionTag);
+ (void) PortalRun(portal, count, false, true, dest, dest, qc);
PortalDrop(portal, false);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index ee5c3a60ff..28130fbc2b 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -787,11 +787,11 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
if (isTempNamespace(get_rel_namespace(rte->relid)))
continue;
- PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
+ PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt));
}
if (plannedstmt->commandType != CMD_SELECT || plannedstmt->hasModifyingCTE)
- PreventCommandIfParallelMode(CreateCommandTag((Node *) plannedstmt));
+ PreventCommandIfParallelMode(CreateCommandName((Node *) plannedstmt));
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 5cff6c4321..9b45a8a9a0 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -530,7 +530,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a SQL function",
- CreateCommandTag(stmt->utilityStmt))));
+ CreateCommandName(stmt->utilityStmt))));
}
if (fcache->readonly_func && !CommandIsReadOnly(stmt))
@@ -538,7 +538,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ CreateCommandName((Node *) stmt))));
/* OK, build the execution_state for this query */
newes = (execution_state *) palloc(sizeof(execution_state));
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index c46764bf42..dfcf2dba80 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1338,7 +1338,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
/* translator: %s is name of a SQL command, eg INSERT */
errmsg("cannot open %s query as cursor",
- plansource->commandTag)));
+ GetCommandTagName(plansource->commandTag))));
}
Assert(list_length(plan->plancache_list) == 1);
@@ -1469,7 +1469,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) pstmt))));
+ CreateCommandName((Node *) pstmt))));
}
}
@@ -2255,7 +2255,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ CreateCommandName((Node *) stmt))));
/*
* If not read-only mode, advance the command counter before each
@@ -2291,9 +2291,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else
{
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
ProcessUtilityContext context;
+ InitializeQueryCompletion(&qc);
+
/*
* If the SPI context is atomic, or we are asked to manage
* snapshots, then we are in an atomic execution context.
@@ -2312,7 +2314,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
paramLI,
_SPI_current->queryEnv,
dest,
- completionTag);
+ &qc);
/* Update "processed" if stmt returned tuples */
if (_SPI_current->tuptable)
@@ -2328,9 +2330,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
{
CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
- if (strncmp(completionTag, "SELECT ", 7) == 0)
- _SPI_current->processed =
- pg_strtouint64(completionTag + 7, NULL, 10);
+ if (qc.commandTag == CMDTAG_SELECT)
+ _SPI_current->processed = qc.nprocessed;
else
{
/*
@@ -2351,9 +2352,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else if (IsA(stmt->utilityStmt, CopyStmt))
{
- Assert(strncmp(completionTag, "COPY ", 5) == 0);
- _SPI_current->processed = pg_strtouint64(completionTag + 5,
- NULL, 10);
+ Assert(qc.commandTag == CMDTAG_COPY);
+ _SPI_current->processed = qc.nprocessed;
}
}
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 5e1dc8a651..295a4b745b 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -100,7 +100,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
buf.record = record;
/* cast so we get a warning when new rmgrs are added */
- switch ((RmgrIds) XLogRecGetRmid(record))
+ switch ((RmgrId) XLogRecGetRmid(record))
{
/*
* Rmgrs we care about for logical decoding. Add new rmgrs in
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index abb533b9d0..ae4a9cbe11 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1074,8 +1074,11 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
static void
DropReplicationSlot(DropReplicationSlotCmd *cmd)
{
+ QueryCompletion qc;
+
ReplicationSlotDrop(cmd->slotname, !cmd->wait);
- EndCommand("DROP_REPLICATION_SLOT", DestRemote);
+ SetQueryCompletion(&qc, CMDTAG_DROP_REPLICATION_SLOT, 0);
+ EndCommand(&qc, DestRemote, false);
}
/*
@@ -1086,6 +1089,7 @@ static void
StartLogicalReplication(StartReplicationCmd *cmd)
{
StringInfoData buf;
+ QueryCompletion qc;
/* make sure that our requirements are still fulfilled */
CheckLogicalDecodingRequirements();
@@ -1160,7 +1164,8 @@ StartLogicalReplication(StartReplicationCmd *cmd)
WalSndSetState(WALSNDSTATE_STARTUP);
/* Get out of COPY mode (CommandComplete). */
- EndCommand("COPY 0", DestRemote);
+ SetQueryCompletion(&qc, CMDTAG_COPY, 0);
+ EndCommand(&qc, DestRemote, false);
}
/*
@@ -1464,6 +1469,7 @@ exec_replication_command(const char *cmd_string)
Node *cmd_node;
MemoryContext cmd_context;
MemoryContext old_context;
+ QueryCompletion qc;
/*
* If WAL sender has been told that shutdown is getting close, switch its
@@ -1614,7 +1620,8 @@ exec_replication_command(const char *cmd_string)
MemoryContextDelete(cmd_context);
/* Send CommandComplete message */
- EndCommand("SELECT", DestRemote);
+ SetQueryCompletion(&qc, CMDTAG_SELECT, 0);
+ EndCommand(&qc, DestRemote, true);
/* Report to pgstat that this process is now idle */
pgstat_report_activity(STATE_IDLE, NULL);
@@ -2867,8 +2874,11 @@ WalSndDone(WalSndSendDataCallback send_data)
if (WalSndCaughtUp && sentPtr == replicatedPtr &&
!pq_is_send_pending())
{
+ QueryCompletion qc;
+
/* Inform the standby that XLOG streaming is done */
- EndCommand("COPY 0", DestRemote);
+ SetQueryCompletion(&qc, CMDTAG_COPY, 0);
+ EndCommand(&qc, DestRemote, false);
pq_flush();
proc_exit(0);
diff --git a/src/backend/tcop/Makefile b/src/backend/tcop/Makefile
index c78f1e0a05..f662a7dd1c 100644
--- a/src/backend/tcop/Makefile
+++ b/src/backend/tcop/Makefile
@@ -13,6 +13,7 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = \
+ cmdtag.o \
dest.o \
fastpath.o \
postgres.o \
diff --git a/src/backend/tcop/cmdtag.c b/src/backend/tcop/cmdtag.c
new file mode 100644
index 0000000000..44583d790f
--- /dev/null
+++ b/src/backend/tcop/cmdtag.c
@@ -0,0 +1,105 @@
+/*-------------------------------------------------------------------------
+ *
+ * cmdtag.c
+ * Data and routines for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/tcop/cmdtag.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "miscadmin.h"
+#include "tcop/cmdtag.h"
+
+
+typedef struct CommandTagBehavior
+{
+ const char *name;
+ const bool event_trigger_ok;
+ const bool table_rewrite_ok;
+ const bool display_rowcount;
+ const bool display_last_oid;
+} CommandTagBehavior;
+
+#define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt, lastoid) \
+ { name, evtrgok, rwrok, rowcnt, lastoid },
+
+const CommandTagBehavior tag_behavior[COMMAND_TAG_NEXTTAG] = {
+#include "tcop/cmdtaglist.h"
+};
+
+#undef PG_CMDTAG
+
+void
+InitializeQueryCompletion(QueryCompletion *qc)
+{
+ qc->commandTag = CMDTAG_UNKNOWN;
+ qc->nprocessed = 0;
+}
+
+const char *
+GetCommandTagName(CommandTag commandTag)
+{
+ return tag_behavior[commandTag].name;
+}
+
+bool
+command_tag_display_last_oid(CommandTag commandTag)
+{
+ return tag_behavior[commandTag].display_last_oid;
+}
+
+bool
+command_tag_display_rowcount(CommandTag commandTag)
+{
+ return tag_behavior[commandTag].display_rowcount;
+}
+
+bool
+command_tag_event_trigger_ok(CommandTag commandTag)
+{
+ return tag_behavior[commandTag].event_trigger_ok;
+}
+
+bool
+command_tag_table_rewrite_ok(CommandTag commandTag)
+{
+ return tag_behavior[commandTag].table_rewrite_ok;
+}
+
+/*
+ * Search CommandTag by name
+ *
+ * Returns CommandTag, or CMDTAG_UNKNOWN if not recognized
+ */
+CommandTag
+GetCommandTagEnum(const char *commandname)
+{
+ const CommandTagBehavior *base,
+ *last,
+ *position;
+ int result;
+
+ if (commandname == NULL || *commandname == '\0')
+ return CMDTAG_UNKNOWN;
+
+ base = tag_behavior;
+ last = tag_behavior + lengthof(tag_behavior) - 1;
+ while (last >= base)
+ {
+ position = base + ((last - base) >> 1);
+ result = pg_strcasecmp(commandname, position->name);
+ if (result == 0)
+ return (CommandTag) (position - tag_behavior);
+ else if (result < 0)
+ last = position - 1;
+ else
+ base = position + 1;
+ }
+ return CMDTAG_UNKNOWN;
+}
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 09c1dcbb53..39dbb2f535 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -100,7 +100,7 @@ DestReceiver *None_Receiver = (DestReceiver *) &donothingDR;
* ----------------
*/
void
-BeginCommand(const char *commandTag, CommandDest dest)
+BeginCommand(CommandTag commandTag, CommandDest dest)
{
/* Nothing to do at present */
}
@@ -163,8 +163,12 @@ CreateDestReceiver(CommandDest dest)
* ----------------
*/
void
-EndCommand(const char *commandTag, CommandDest dest)
+EndCommand(const QueryCompletion *qc, CommandDest dest, bool force_undecorated_output)
{
+ char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag tag;
+ const char *tagname;
+
switch (dest)
{
case DestRemote:
@@ -172,11 +176,38 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestRemoteSimple:
/*
- * We assume the commandTag is plain ASCII and therefore requires
- * no encoding conversion.
+ * We assume the tagname is plain ASCII and therefore requires no
+ * encoding conversion.
+ *
+ * We no longer display LastOid, but to preserve the wire
+ * protocol, we write InvalidOid where the LastOid used to be
+ * written.
+ *
+ * All cases where LastOid was written also write nprocessed
+ * count, so just Assert that rather than having an extra test.
*/
- pq_putmessage('C', commandTag, strlen(commandTag) + 1);
- break;
+ tag = qc->commandTag;
+ tagname = GetCommandTagName(tag);
+
+ if (command_tag_display_last_oid(tag) && !force_undecorated_output)
+ {
+ /* We assume InvalidOid is zero in the snprintf format string */
+ Assert(InvalidOid == 0);
+
+ /*
+ * We assume rowcount will always be wanted when last_oid is
+ * wanted
+ */
+ Assert(command_tag_display_rowcount(tag));
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "%s 0 " UINT64_FORMAT, tagname, qc->nprocessed);
+ }
+ else if (command_tag_display_rowcount(tag) && !force_undecorated_output)
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ "%s " UINT64_FORMAT, tagname, qc->nprocessed);
+ else
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s", tagname);
+ pq_putmessage('C', completionTag, strlen(completionTag) + 1);
case DestNone:
case DestDebug:
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 23661ae15f..e5ec388296 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1064,8 +1064,8 @@ exec_simple_query(const char *query_string)
{
RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);
bool snapshot_set = false;
- const char *commandTag;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag commandTag;
+ QueryCompletion qc;
MemoryContext per_parsetree_context = NULL;
List *querytree_list,
*plantree_list;
@@ -1081,7 +1081,7 @@ exec_simple_query(const char *query_string)
*/
commandTag = CreateCommandTag(parsetree->stmt);
- set_ps_display(commandTag, false);
+ set_ps_display(GetCommandTagName(commandTag), false);
BeginCommand(commandTag, dest);
@@ -1239,7 +1239,7 @@ exec_simple_query(const char *query_string)
true,
receiver,
receiver,
- completionTag);
+ &qc);
receiver->rDestroy(receiver);
@@ -1290,7 +1290,7 @@ exec_simple_query(const char *query_string)
* command the client sent, regardless of rewriting. (But a command
* aborted by error will not send an EndCommand report at all.)
*/
- EndCommand(completionTag, dest);
+ EndCommand(&qc, dest, false);
/* Now we may drop the per-parsetree context, if one was created. */
if (per_parsetree_context)
@@ -1352,7 +1352,7 @@ exec_parse_message(const char *query_string, /* string to execute */
MemoryContext oldcontext;
List *parsetree_list;
RawStmt *raw_parse_tree;
- const char *commandTag;
+ CommandTag commandTag;
List *querytree_list;
CachedPlanSource *psrc;
bool is_named;
@@ -1514,7 +1514,7 @@ exec_parse_message(const char *query_string, /* string to execute */
{
/* Empty input string. This is legal. */
raw_parse_tree = NULL;
- commandTag = NULL;
+ commandTag = CMDTAG_UNKNOWN;
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
querytree_list = NIL;
}
@@ -2031,7 +2031,7 @@ exec_execute_message(const char *portal_name, long max_rows)
DestReceiver *receiver;
Portal portal;
bool completed;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
const char *sourceText;
const char *prepStmtName;
ParamListInfo portalParams;
@@ -2058,7 +2058,7 @@ exec_execute_message(const char *portal_name, long max_rows)
* If the original query was a null string, just return
* EmptyQueryResponse.
*/
- if (portal->commandTag == NULL)
+ if (portal->commandTag == CMDTAG_UNKNOWN)
{
Assert(portal->stmts == NIL);
NullCommand(dest);
@@ -2104,7 +2104,7 @@ exec_execute_message(const char *portal_name, long max_rows)
pgstat_report_activity(STATE_RUNNING, sourceText);
- set_ps_display(portal->commandTag, false);
+ set_ps_display(GetCommandTagName(portal->commandTag), false);
if (save_log_statement_stats)
ResetUsage();
@@ -2185,7 +2185,7 @@ exec_execute_message(const char *portal_name, long max_rows)
!execute_is_fetch && max_rows == FETCH_ALL,
receiver,
receiver,
- completionTag);
+ &qc);
receiver->rDestroy(receiver);
@@ -2218,7 +2218,7 @@ exec_execute_message(const char *portal_name, long max_rows)
}
/* Send appropriate CommandComplete to client */
- EndCommand(completionTag, dest);
+ EndCommand(&qc, dest, false);
}
else
{
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 0f5801e046..5781fb2e55 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -40,7 +40,7 @@ static void ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletion *qc);
static void FillPortalStore(Portal portal, bool isTopLevel);
static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
DestReceiver *dest);
@@ -48,11 +48,11 @@ static uint64 PortalRunSelect(Portal portal, bool forward, long count,
DestReceiver *dest);
static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
static void PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletion *qc);
static uint64 DoPortalRunFetch(Portal portal,
FetchDirection fdirection,
long count,
@@ -125,10 +125,9 @@ FreeQueryDesc(QueryDesc *qdesc)
* sourceText: the source text of the query
* params: any parameters needed
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store the command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want a status string.
*
* Must be called in a memory context that will be reset or deleted on
* error; otherwise the executor's memory usage will be leaked.
@@ -139,7 +138,7 @@ ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
QueryDesc *queryDesc;
@@ -161,38 +160,26 @@ ProcessQuery(PlannedStmt *plan,
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
/*
- * Build command completion status string, if caller wants one.
+ * Build command completion status data, if caller wants one.
*/
- if (completionTag)
+ if (qc)
{
- Oid lastOid;
-
switch (queryDesc->operation)
{
case CMD_SELECT:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, CMDTAG_SELECT, queryDesc->estate->es_processed);
break;
case CMD_INSERT:
- /* lastoid doesn't exist anymore */
- lastOid = InvalidOid;
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "INSERT %u " UINT64_FORMAT,
- lastOid, queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, CMDTAG_INSERT, queryDesc->estate->es_processed);
break;
case CMD_UPDATE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "UPDATE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, CMDTAG_UPDATE, queryDesc->estate->es_processed);
break;
case CMD_DELETE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "DELETE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, CMDTAG_DELETE, queryDesc->estate->es_processed);
break;
default:
- strcpy(completionTag, "???");
+ SetQueryCompletion(qc, CMDTAG_UNKNOWN, queryDesc->estate->es_processed);
break;
}
}
@@ -675,9 +662,8 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
*
* altdest: where to send output of non-primary queries
*
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
- * May be NULL if caller doesn't want a status string.
+ * qc: where to store command completion status data.
+ * May be NULL if caller doesn't want status data.
*
* Returns true if the portal's execution is complete, false if it was
* suspended due to exhaustion of the count parameter.
@@ -685,7 +671,7 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
bool
PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletion *qc)
{
bool result;
uint64 nprocessed;
@@ -700,9 +686,9 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
TRACE_POSTGRESQL_QUERY_EXECUTE_START();
- /* Initialize completion tag to empty string */
- if (completionTag)
- completionTag[0] = '\0';
+ /* Initialize empty completion data */
+ if (qc)
+ InitializeQueryCompletion(qc);
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
{
@@ -771,16 +757,13 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
/*
* If the portal result contains a command tag and the caller
- * gave us a pointer to store it, copy it. Patch the "SELECT"
- * tag to also provide the rowcount.
+ * gave us a pointer to store it, copy it and update the
+ * rowcount.
*/
- if (completionTag && portal->commandTag)
+ if (qc && portal->qc.commandTag != CMDTAG_UNKNOWN)
{
- if (strcmp(portal->commandTag, "SELECT") == 0)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT, nprocessed);
- else
- strcpy(completionTag, portal->commandTag);
+ CopyQueryCompletion(qc, &portal->qc);
+ qc->nprocessed = nprocessed;
}
/* Mark portal not active */
@@ -794,7 +777,7 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
case PORTAL_MULTI_QUERY:
PortalRunMulti(portal, isTopLevel, false,
- dest, altdest, completionTag);
+ dest, altdest, qc);
/* Prevent portal's commands from being re-executed */
MarkPortalDone(portal);
@@ -1005,8 +988,9 @@ static void
FillPortalStore(Portal portal, bool isTopLevel)
{
DestReceiver *treceiver;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
+ InitializeQueryCompletion(&qc);
PortalCreateHoldStore(portal);
treceiver = CreateDestReceiver(DestTuplestore);
SetTuplestoreDestReceiverParams(treceiver,
@@ -1014,8 +998,6 @@ FillPortalStore(Portal portal, bool isTopLevel)
portal->holdContext,
false);
- completionTag[0] = '\0';
-
switch (portal->strategy)
{
case PORTAL_ONE_RETURNING:
@@ -1028,12 +1010,12 @@ FillPortalStore(Portal portal, bool isTopLevel)
* portal's holdSnapshot to the snapshot used (or a copy of it).
*/
PortalRunMulti(portal, isTopLevel, true,
- treceiver, None_Receiver, completionTag);
+ treceiver, None_Receiver, &qc);
break;
case PORTAL_UTIL_SELECT:
PortalRunUtility(portal, linitial_node(PlannedStmt, portal->stmts),
- isTopLevel, true, treceiver, completionTag);
+ isTopLevel, true, treceiver, &qc);
break;
default:
@@ -1042,9 +1024,9 @@ FillPortalStore(Portal portal, bool isTopLevel)
break;
}
- /* Override default completion tag with actual command result */
- if (completionTag[0] != '\0')
- portal->commandTag = pstrdup(completionTag);
+ /* Override portal completion data with actual command results */
+ if (qc.commandTag != CMDTAG_UNKNOWN)
+ CopyQueryCompletion(&portal->qc, &qc);
treceiver->rDestroy(treceiver);
}
@@ -1130,7 +1112,7 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count,
static void
PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
Node *utilityStmt = pstmt->utilityStmt;
Snapshot snapshot;
@@ -1178,7 +1160,7 @@ PortalRunUtility(Portal portal, PlannedStmt *pstmt,
portal->portalParams,
portal->queryEnv,
dest,
- completionTag);
+ qc);
/* Some utility statements may change context on us */
MemoryContextSwitchTo(portal->portalContext);
@@ -1202,7 +1184,7 @@ static void
PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletion *qc)
{
bool active_snapshot_set = false;
ListCell *stmtlist_item;
@@ -1284,7 +1266,7 @@ PortalRunMulti(Portal portal,
portal->sourceText,
portal->portalParams,
portal->queryEnv,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1319,7 +1301,7 @@ PortalRunMulti(Portal portal,
Assert(!active_snapshot_set);
/* statement can set tag string */
PortalRunUtility(portal, pstmt, isTopLevel, false,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1350,8 +1332,8 @@ PortalRunMulti(Portal portal,
PopActiveSnapshot();
/*
- * If a command completion tag was supplied, use it. Otherwise use the
- * portal's commandTag as the default completion tag.
+ * If a query completion data was supplied, use it. Otherwise use the
+ * portal's query completion data.
*
* Exception: Clients expect INSERT/UPDATE/DELETE tags to have counts, so
* fake them with zeros. This can happen with DO INSTEAD rules if there
@@ -1361,18 +1343,12 @@ PortalRunMulti(Portal portal,
* e.g. an INSERT that does an UPDATE instead should not print "0 1" if
* one row was updated. See QueryRewrite(), step 3, for details.
*/
- if (completionTag && completionTag[0] == '\0')
+ if (qc && qc->commandTag == CMDTAG_UNKNOWN)
{
- if (portal->commandTag)
- strcpy(completionTag, portal->commandTag);
- if (strcmp(completionTag, "SELECT") == 0)
- sprintf(completionTag, "SELECT 0 0");
- else if (strcmp(completionTag, "INSERT") == 0)
- strcpy(completionTag, "INSERT 0 0");
- else if (strcmp(completionTag, "UPDATE") == 0)
- strcpy(completionTag, "UPDATE 0");
- else if (strcmp(completionTag, "DELETE") == 0)
- strcpy(completionTag, "DELETE 0");
+ if (portal->qc.commandTag != CMDTAG_UNKNOWN)
+ CopyQueryCompletion(qc, &portal->qc);
+ /* If the caller supplied a qc, we should have set it by now. */
+ Assert(qc->commandTag != CMDTAG_UNKNOWN);
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index bb85b5e52a..e9f4820b77 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -75,7 +75,7 @@
ProcessUtility_hook_type ProcessUtility_hook = NULL;
/* local function declarations */
-static int ClassifyUtilityCommandAsReadOnly(Node *parsetree);
+static int ClassifyUtilityCommandAsReadOnly(Node *parsetree);
static void ProcessUtilitySlow(ParseState *pstate,
PlannedStmt *pstmt,
const char *queryString,
@@ -83,10 +83,9 @@ static void ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletion *qc);
static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
-
/*
* CommandIsReadOnly: is an executable query read-only?
*
@@ -467,7 +466,6 @@ CheckRestrictedOperation(const char *cmdname)
cmdname)));
}
-
/*
* ProcessUtility
* general utility function invoker
@@ -480,16 +478,13 @@ CheckRestrictedOperation(const char *cmdname)
* queryEnv: environment for parse through execution (e.g., ephemeral named
* tables like trigger transition tables). May be NULL.
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store command completion status data.
*
* Caller MUST supply a queryString; it is not allowed (anymore) to pass NULL.
* If you really don't have source text, you can pass a constant string,
* perhaps "(query not available)".
*
- * completionTag is only set nonempty if we want to return a nondefault status.
- *
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*
* Note for users of ProcessUtility_hook: the same queryString may be passed
* to multiple invocations of ProcessUtility when processing a query string
@@ -507,7 +502,7 @@ ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Assert(IsA(pstmt, PlannedStmt));
Assert(pstmt->commandType == CMD_UTILITY);
@@ -521,11 +516,11 @@ ProcessUtility(PlannedStmt *pstmt,
if (ProcessUtility_hook)
(*ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
/*
@@ -546,7 +541,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -562,18 +557,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (readonly_flags != COMMAND_IS_STRICTLY_READ_ONLY &&
(XactReadOnly || IsInParallelMode()))
{
- const char *commandtag = CreateCommandTag(parsetree);
+ CommandTag commandtag = CreateCommandTag(parsetree);
if ((readonly_flags & COMMAND_OK_IN_READ_ONLY_TXN) == 0)
- PreventCommandIfReadOnly(commandtag);
+ PreventCommandIfReadOnly(GetCommandTagName(commandtag));
if ((readonly_flags & COMMAND_OK_IN_PARALLEL_MODE) == 0)
- PreventCommandIfParallelMode(commandtag);
+ PreventCommandIfParallelMode(GetCommandTagName(commandtag));
if ((readonly_flags & COMMAND_OK_IN_RECOVERY) == 0)
- PreventCommandDuringRecovery(commandtag);
+ PreventCommandDuringRecovery(GetCommandTagName(commandtag));
}
- if (completionTag)
- completionTag[0] = '\0';
+ if (qc)
+ InitializeQueryCompletion(qc);
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -623,18 +618,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case TRANS_STMT_COMMIT:
if (!EndTransactionBlock(stmt->chain))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQueryCompletion(qc, CMDTAG_ROLLBACK, 0);
}
break;
case TRANS_STMT_PREPARE:
if (!PrepareTransactionBlock(stmt->gid))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQueryCompletion(qc, CMDTAG_ROLLBACK, 0);
}
break;
@@ -693,8 +688,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case T_FetchStmt:
- PerformPortalFetch((FetchStmt *) parsetree, dest,
- completionTag);
+ PerformPortalFetch((FetchStmt *) parsetree, dest, qc);
break;
case T_DoStmt:
@@ -729,9 +723,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
DoCopy(pstate, (CopyStmt *) parsetree,
pstmt->stmt_location, pstmt->stmt_len,
&processed);
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "COPY " UINT64_FORMAT, processed);
+ if (qc)
+ SetQueryCompletion(qc, CMDTAG_COPY, processed);
}
break;
@@ -745,7 +738,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ExecuteQuery(pstate,
(ExecuteStmt *) parsetree, NULL,
params,
- dest, completionTag);
+ dest, qc);
break;
case T_DeallocateStmt:
@@ -974,7 +967,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecuteGrantStmt(stmt);
}
@@ -987,7 +980,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecDropStmt(stmt, isTopLevel);
}
@@ -1000,7 +993,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecRenameStmt(stmt);
}
@@ -1013,7 +1006,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectDependsStmt(stmt, NULL);
}
@@ -1026,7 +1019,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
}
@@ -1039,7 +1032,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterOwnerStmt(stmt);
}
@@ -1052,7 +1045,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
CommentObject(stmt);
break;
@@ -1065,7 +1058,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecSecLabelStmt(stmt);
break;
@@ -1075,7 +1068,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
break;
}
@@ -1102,7 +1095,7 @@ ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -1605,7 +1598,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateTableAsStmt:
address = ExecCreateTableAs(pstate, (CreateTableAsStmt *) parsetree,
- params, queryEnv, completionTag);
+ params, queryEnv, qc);
break;
case T_RefreshMatViewStmt:
@@ -1620,7 +1613,7 @@ ProcessUtilitySlow(ParseState *pstate,
PG_TRY();
{
address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
- queryString, params, completionTag);
+ queryString, params, qc);
}
PG_FINALLY();
{
@@ -2099,137 +2092,137 @@ UtilityContainsQuery(Node *parsetree)
*
* This covers most cases where ALTER is used with an ObjectType enum.
*/
-static const char *
+static CommandTag
AlterObjectTypeCommandTag(ObjectType objtype)
{
- const char *tag;
+ CommandTag tag;
switch (objtype)
{
case OBJECT_AGGREGATE:
- tag = "ALTER AGGREGATE";
+ tag = CMDTAG_ALTER_AGGREGATE;
break;
case OBJECT_ATTRIBUTE:
- tag = "ALTER TYPE";
+ tag = CMDTAG_ALTER_TYPE;
break;
case OBJECT_CAST:
- tag = "ALTER CAST";
+ tag = CMDTAG_ALTER_CAST;
break;
case OBJECT_COLLATION:
- tag = "ALTER COLLATION";
+ tag = CMDTAG_ALTER_COLLATION;
break;
case OBJECT_COLUMN:
- tag = "ALTER TABLE";
+ tag = CMDTAG_ALTER_TABLE;
break;
case OBJECT_CONVERSION:
- tag = "ALTER CONVERSION";
+ tag = CMDTAG_ALTER_CONVERSION;
break;
case OBJECT_DATABASE:
- tag = "ALTER DATABASE";
+ tag = CMDTAG_ALTER_DATABASE;
break;
case OBJECT_DOMAIN:
case OBJECT_DOMCONSTRAINT:
- tag = "ALTER DOMAIN";
+ tag = CMDTAG_ALTER_DOMAIN;
break;
case OBJECT_EXTENSION:
- tag = "ALTER EXTENSION";
+ tag = CMDTAG_ALTER_EXTENSION;
break;
case OBJECT_FDW:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = CMDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "ALTER SERVER";
+ tag = CMDTAG_ALTER_SERVER;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "ALTER FOREIGN TABLE";
+ tag = CMDTAG_ALTER_FOREIGN_TABLE;
break;
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = CMDTAG_ALTER_FUNCTION;
break;
case OBJECT_INDEX:
- tag = "ALTER INDEX";
+ tag = CMDTAG_ALTER_INDEX;
break;
case OBJECT_LANGUAGE:
- tag = "ALTER LANGUAGE";
+ tag = CMDTAG_ALTER_LANGUAGE;
break;
case OBJECT_LARGEOBJECT:
- tag = "ALTER LARGE OBJECT";
+ tag = CMDTAG_ALTER_LARGE_OBJECT;
break;
case OBJECT_OPCLASS:
- tag = "ALTER OPERATOR CLASS";
+ tag = CMDTAG_ALTER_OPERATOR_CLASS;
break;
case OBJECT_OPERATOR:
- tag = "ALTER OPERATOR";
+ tag = CMDTAG_ALTER_OPERATOR;
break;
case OBJECT_OPFAMILY:
- tag = "ALTER OPERATOR FAMILY";
+ tag = CMDTAG_ALTER_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "ALTER POLICY";
+ tag = CMDTAG_ALTER_POLICY;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = CMDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROLE:
- tag = "ALTER ROLE";
+ tag = CMDTAG_ALTER_ROLE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = CMDTAG_ALTER_ROUTINE;
break;
case OBJECT_RULE:
- tag = "ALTER RULE";
+ tag = CMDTAG_ALTER_RULE;
break;
case OBJECT_SCHEMA:
- tag = "ALTER SCHEMA";
+ tag = CMDTAG_ALTER_SCHEMA;
break;
case OBJECT_SEQUENCE:
- tag = "ALTER SEQUENCE";
+ tag = CMDTAG_ALTER_SEQUENCE;
break;
case OBJECT_TABLE:
case OBJECT_TABCONSTRAINT:
- tag = "ALTER TABLE";
+ tag = CMDTAG_ALTER_TABLE;
break;
case OBJECT_TABLESPACE:
- tag = "ALTER TABLESPACE";
+ tag = CMDTAG_ALTER_TABLESPACE;
break;
case OBJECT_TRIGGER:
- tag = "ALTER TRIGGER";
+ tag = CMDTAG_ALTER_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "ALTER EVENT TRIGGER";
+ tag = CMDTAG_ALTER_EVENT_TRIGGER;
break;
case OBJECT_TSCONFIGURATION:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_TSDICTIONARY:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSPARSER:
- tag = "ALTER TEXT SEARCH PARSER";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSTEMPLATE:
- tag = "ALTER TEXT SEARCH TEMPLATE";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TYPE:
- tag = "ALTER TYPE";
+ tag = CMDTAG_ALTER_TYPE;
break;
case OBJECT_VIEW:
- tag = "ALTER VIEW";
+ tag = CMDTAG_ALTER_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "ALTER MATERIALIZED VIEW";
+ tag = CMDTAG_ALTER_MATERIALIZED_VIEW;
break;
case OBJECT_PUBLICATION:
- tag = "ALTER PUBLICATION";
+ tag = CMDTAG_ALTER_PUBLICATION;
break;
case OBJECT_SUBSCRIPTION:
- tag = "ALTER SUBSCRIPTION";
+ tag = CMDTAG_ALTER_SUBSCRIPTION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "ALTER STATISTICS";
+ tag = CMDTAG_ALTER_STATISTICS;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
@@ -2238,20 +2231,17 @@ AlterObjectTypeCommandTag(ObjectType objtype)
/*
* CreateCommandTag
- * utility to get a string representation of the command operation,
+ * utility to get a CommandTag for the command operation,
* given either a raw (un-analyzed) parsetree, an analyzed Query,
* or a PlannedStmt.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
- *
- * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
- * Also, the result must point at a true constant (permanent storage).
*/
-const char *
+CommandTag
CreateCommandTag(Node *parsetree)
{
- const char *tag;
+ CommandTag tag;
switch (nodeTag(parsetree))
{
@@ -2262,19 +2252,19 @@ CreateCommandTag(Node *parsetree)
/* raw plannable queries */
case T_InsertStmt:
- tag = "INSERT";
+ tag = CMDTAG_INSERT;
break;
case T_DeleteStmt:
- tag = "DELETE";
+ tag = CMDTAG_DELETE;
break;
case T_UpdateStmt:
- tag = "UPDATE";
+ tag = CMDTAG_UPDATE;
break;
case T_SelectStmt:
- tag = "SELECT";
+ tag = CMDTAG_SELECT;
break;
/* utility statements --- same whether raw or cooked */
@@ -2285,51 +2275,51 @@ CreateCommandTag(Node *parsetree)
switch (stmt->kind)
{
case TRANS_STMT_BEGIN:
- tag = "BEGIN";
+ tag = CMDTAG_BEGIN;
break;
case TRANS_STMT_START:
- tag = "START TRANSACTION";
+ tag = CMDTAG_START_TRANSACTION;
break;
case TRANS_STMT_COMMIT:
- tag = "COMMIT";
+ tag = CMDTAG_COMMIT;
break;
case TRANS_STMT_ROLLBACK:
case TRANS_STMT_ROLLBACK_TO:
- tag = "ROLLBACK";
+ tag = CMDTAG_ROLLBACK;
break;
case TRANS_STMT_SAVEPOINT:
- tag = "SAVEPOINT";
+ tag = CMDTAG_SAVEPOINT;
break;
case TRANS_STMT_RELEASE:
- tag = "RELEASE";
+ tag = CMDTAG_RELEASE;
break;
case TRANS_STMT_PREPARE:
- tag = "PREPARE TRANSACTION";
+ tag = CMDTAG_PREPARE_TRANSACTION;
break;
case TRANS_STMT_COMMIT_PREPARED:
- tag = "COMMIT PREPARED";
+ tag = CMDTAG_COMMIT_PREPARED;
break;
case TRANS_STMT_ROLLBACK_PREPARED:
- tag = "ROLLBACK PREPARED";
+ tag = CMDTAG_ROLLBACK_PREPARED;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
}
break;
case T_DeclareCursorStmt:
- tag = "DECLARE CURSOR";
+ tag = CMDTAG_DECLARE_CURSOR;
break;
case T_ClosePortalStmt:
@@ -2337,9 +2327,9 @@ CreateCommandTag(Node *parsetree)
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
if (stmt->portalname == NULL)
- tag = "CLOSE CURSOR ALL";
+ tag = CMDTAG_CLOSE_CURSOR_ALL;
else
- tag = "CLOSE CURSOR";
+ tag = CMDTAG_CLOSE_CURSOR;
}
break;
@@ -2347,209 +2337,209 @@ CreateCommandTag(Node *parsetree)
{
FetchStmt *stmt = (FetchStmt *) parsetree;
- tag = (stmt->ismove) ? "MOVE" : "FETCH";
+ tag = (stmt->ismove) ? CMDTAG_MOVE : CMDTAG_FETCH;
}
break;
case T_CreateDomainStmt:
- tag = "CREATE DOMAIN";
+ tag = CMDTAG_CREATE_DOMAIN;
break;
case T_CreateSchemaStmt:
- tag = "CREATE SCHEMA";
+ tag = CMDTAG_CREATE_SCHEMA;
break;
case T_CreateStmt:
- tag = "CREATE TABLE";
+ tag = CMDTAG_CREATE_TABLE;
break;
case T_CreateTableSpaceStmt:
- tag = "CREATE TABLESPACE";
+ tag = CMDTAG_CREATE_TABLESPACE;
break;
case T_DropTableSpaceStmt:
- tag = "DROP TABLESPACE";
+ tag = CMDTAG_DROP_TABLESPACE;
break;
case T_AlterTableSpaceOptionsStmt:
- tag = "ALTER TABLESPACE";
+ tag = CMDTAG_ALTER_TABLESPACE;
break;
case T_CreateExtensionStmt:
- tag = "CREATE EXTENSION";
+ tag = CMDTAG_CREATE_EXTENSION;
break;
case T_AlterExtensionStmt:
- tag = "ALTER EXTENSION";
+ tag = CMDTAG_ALTER_EXTENSION;
break;
case T_AlterExtensionContentsStmt:
- tag = "ALTER EXTENSION";
+ tag = CMDTAG_ALTER_EXTENSION;
break;
case T_CreateFdwStmt:
- tag = "CREATE FOREIGN DATA WRAPPER";
+ tag = CMDTAG_CREATE_FOREIGN_DATA_WRAPPER;
break;
case T_AlterFdwStmt:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = CMDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case T_CreateForeignServerStmt:
- tag = "CREATE SERVER";
+ tag = CMDTAG_CREATE_SERVER;
break;
case T_AlterForeignServerStmt:
- tag = "ALTER SERVER";
+ tag = CMDTAG_ALTER_SERVER;
break;
case T_CreateUserMappingStmt:
- tag = "CREATE USER MAPPING";
+ tag = CMDTAG_CREATE_USER_MAPPING;
break;
case T_AlterUserMappingStmt:
- tag = "ALTER USER MAPPING";
+ tag = CMDTAG_ALTER_USER_MAPPING;
break;
case T_DropUserMappingStmt:
- tag = "DROP USER MAPPING";
+ tag = CMDTAG_DROP_USER_MAPPING;
break;
case T_CreateForeignTableStmt:
- tag = "CREATE FOREIGN TABLE";
+ tag = CMDTAG_CREATE_FOREIGN_TABLE;
break;
case T_ImportForeignSchemaStmt:
- tag = "IMPORT FOREIGN SCHEMA";
+ tag = CMDTAG_IMPORT_FOREIGN_SCHEMA;
break;
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_TABLE:
- tag = "DROP TABLE";
+ tag = CMDTAG_DROP_TABLE;
break;
case OBJECT_SEQUENCE:
- tag = "DROP SEQUENCE";
+ tag = CMDTAG_DROP_SEQUENCE;
break;
case OBJECT_VIEW:
- tag = "DROP VIEW";
+ tag = CMDTAG_DROP_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "DROP MATERIALIZED VIEW";
+ tag = CMDTAG_DROP_MATERIALIZED_VIEW;
break;
case OBJECT_INDEX:
- tag = "DROP INDEX";
+ tag = CMDTAG_DROP_INDEX;
break;
case OBJECT_TYPE:
- tag = "DROP TYPE";
+ tag = CMDTAG_DROP_TYPE;
break;
case OBJECT_DOMAIN:
- tag = "DROP DOMAIN";
+ tag = CMDTAG_DROP_DOMAIN;
break;
case OBJECT_COLLATION:
- tag = "DROP COLLATION";
+ tag = CMDTAG_DROP_COLLATION;
break;
case OBJECT_CONVERSION:
- tag = "DROP CONVERSION";
+ tag = CMDTAG_DROP_CONVERSION;
break;
case OBJECT_SCHEMA:
- tag = "DROP SCHEMA";
+ tag = CMDTAG_DROP_SCHEMA;
break;
case OBJECT_TSPARSER:
- tag = "DROP TEXT SEARCH PARSER";
+ tag = CMDTAG_DROP_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "DROP TEXT SEARCH DICTIONARY";
+ tag = CMDTAG_DROP_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "DROP TEXT SEARCH TEMPLATE";
+ tag = CMDTAG_DROP_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "DROP TEXT SEARCH CONFIGURATION";
+ tag = CMDTAG_DROP_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "DROP FOREIGN TABLE";
+ tag = CMDTAG_DROP_FOREIGN_TABLE;
break;
case OBJECT_EXTENSION:
- tag = "DROP EXTENSION";
+ tag = CMDTAG_DROP_EXTENSION;
break;
case OBJECT_FUNCTION:
- tag = "DROP FUNCTION";
+ tag = CMDTAG_DROP_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "DROP PROCEDURE";
+ tag = CMDTAG_DROP_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "DROP ROUTINE";
+ tag = CMDTAG_DROP_ROUTINE;
break;
case OBJECT_AGGREGATE:
- tag = "DROP AGGREGATE";
+ tag = CMDTAG_DROP_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "DROP OPERATOR";
+ tag = CMDTAG_DROP_OPERATOR;
break;
case OBJECT_LANGUAGE:
- tag = "DROP LANGUAGE";
+ tag = CMDTAG_DROP_LANGUAGE;
break;
case OBJECT_CAST:
- tag = "DROP CAST";
+ tag = CMDTAG_DROP_CAST;
break;
case OBJECT_TRIGGER:
- tag = "DROP TRIGGER";
+ tag = CMDTAG_DROP_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "DROP EVENT TRIGGER";
+ tag = CMDTAG_DROP_EVENT_TRIGGER;
break;
case OBJECT_RULE:
- tag = "DROP RULE";
+ tag = CMDTAG_DROP_RULE;
break;
case OBJECT_FDW:
- tag = "DROP FOREIGN DATA WRAPPER";
+ tag = CMDTAG_DROP_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "DROP SERVER";
+ tag = CMDTAG_DROP_SERVER;
break;
case OBJECT_OPCLASS:
- tag = "DROP OPERATOR CLASS";
+ tag = CMDTAG_DROP_OPERATOR_CLASS;
break;
case OBJECT_OPFAMILY:
- tag = "DROP OPERATOR FAMILY";
+ tag = CMDTAG_DROP_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "DROP POLICY";
+ tag = CMDTAG_DROP_POLICY;
break;
case OBJECT_TRANSFORM:
- tag = "DROP TRANSFORM";
+ tag = CMDTAG_DROP_TRANSFORM;
break;
case OBJECT_ACCESS_METHOD:
- tag = "DROP ACCESS METHOD";
+ tag = CMDTAG_DROP_ACCESS_METHOD;
break;
case OBJECT_PUBLICATION:
- tag = "DROP PUBLICATION";
+ tag = CMDTAG_DROP_PUBLICATION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "DROP STATISTICS";
+ tag = CMDTAG_DROP_STATISTICS;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
case T_TruncateStmt:
- tag = "TRUNCATE TABLE";
+ tag = CMDTAG_TRUNCATE_TABLE;
break;
case T_CommentStmt:
- tag = "COMMENT";
+ tag = CMDTAG_COMMENT;
break;
case T_SecLabelStmt:
- tag = "SECURITY LABEL";
+ tag = CMDTAG_SECURITY_LABEL;
break;
case T_CopyStmt:
- tag = "COPY";
+ tag = CMDTAG_COPY;
break;
case T_RenameStmt:
@@ -2584,23 +2574,23 @@ CreateCommandTag(Node *parsetree)
break;
case T_AlterDomainStmt:
- tag = "ALTER DOMAIN";
+ tag = CMDTAG_ALTER_DOMAIN;
break;
case T_AlterFunctionStmt:
switch (((AlterFunctionStmt *) parsetree)->objtype)
{
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = CMDTAG_ALTER_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = CMDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = CMDTAG_ALTER_ROUTINE;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
@@ -2608,7 +2598,7 @@ CreateCommandTag(Node *parsetree)
{
GrantStmt *stmt = (GrantStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
+ tag = (stmt->is_grant) ? CMDTAG_GRANT : CMDTAG_REVOKE;
}
break;
@@ -2616,145 +2606,145 @@ CreateCommandTag(Node *parsetree)
{
GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
+ tag = (stmt->is_grant) ? CMDTAG_GRANT_ROLE : CMDTAG_REVOKE_ROLE;
}
break;
case T_AlterDefaultPrivilegesStmt:
- tag = "ALTER DEFAULT PRIVILEGES";
+ tag = CMDTAG_ALTER_DEFAULT_PRIVILEGES;
break;
case T_DefineStmt:
switch (((DefineStmt *) parsetree)->kind)
{
case OBJECT_AGGREGATE:
- tag = "CREATE AGGREGATE";
+ tag = CMDTAG_CREATE_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "CREATE OPERATOR";
+ tag = CMDTAG_CREATE_OPERATOR;
break;
case OBJECT_TYPE:
- tag = "CREATE TYPE";
+ tag = CMDTAG_CREATE_TYPE;
break;
case OBJECT_TSPARSER:
- tag = "CREATE TEXT SEARCH PARSER";
+ tag = CMDTAG_CREATE_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "CREATE TEXT SEARCH DICTIONARY";
+ tag = CMDTAG_CREATE_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "CREATE TEXT SEARCH TEMPLATE";
+ tag = CMDTAG_CREATE_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "CREATE TEXT SEARCH CONFIGURATION";
+ tag = CMDTAG_CREATE_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_COLLATION:
- tag = "CREATE COLLATION";
+ tag = CMDTAG_CREATE_COLLATION;
break;
case OBJECT_ACCESS_METHOD:
- tag = "CREATE ACCESS METHOD";
+ tag = CMDTAG_CREATE_ACCESS_METHOD;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
case T_CompositeTypeStmt:
- tag = "CREATE TYPE";
+ tag = CMDTAG_CREATE_TYPE;
break;
case T_CreateEnumStmt:
- tag = "CREATE TYPE";
+ tag = CMDTAG_CREATE_TYPE;
break;
case T_CreateRangeStmt:
- tag = "CREATE TYPE";
+ tag = CMDTAG_CREATE_TYPE;
break;
case T_AlterEnumStmt:
- tag = "ALTER TYPE";
+ tag = CMDTAG_ALTER_TYPE;
break;
case T_ViewStmt:
- tag = "CREATE VIEW";
+ tag = CMDTAG_CREATE_VIEW;
break;
case T_CreateFunctionStmt:
if (((CreateFunctionStmt *) parsetree)->is_procedure)
- tag = "CREATE PROCEDURE";
+ tag = CMDTAG_CREATE_PROCEDURE;
else
- tag = "CREATE FUNCTION";
+ tag = CMDTAG_CREATE_FUNCTION;
break;
case T_IndexStmt:
- tag = "CREATE INDEX";
+ tag = CMDTAG_CREATE_INDEX;
break;
case T_RuleStmt:
- tag = "CREATE RULE";
+ tag = CMDTAG_CREATE_RULE;
break;
case T_CreateSeqStmt:
- tag = "CREATE SEQUENCE";
+ tag = CMDTAG_CREATE_SEQUENCE;
break;
case T_AlterSeqStmt:
- tag = "ALTER SEQUENCE";
+ tag = CMDTAG_ALTER_SEQUENCE;
break;
case T_DoStmt:
- tag = "DO";
+ tag = CMDTAG_DO;
break;
case T_CreatedbStmt:
- tag = "CREATE DATABASE";
+ tag = CMDTAG_CREATE_DATABASE;
break;
case T_AlterDatabaseStmt:
- tag = "ALTER DATABASE";
+ tag = CMDTAG_ALTER_DATABASE;
break;
case T_AlterDatabaseSetStmt:
- tag = "ALTER DATABASE";
+ tag = CMDTAG_ALTER_DATABASE;
break;
case T_DropdbStmt:
- tag = "DROP DATABASE";
+ tag = CMDTAG_DROP_DATABASE;
break;
case T_NotifyStmt:
- tag = "NOTIFY";
+ tag = CMDTAG_NOTIFY;
break;
case T_ListenStmt:
- tag = "LISTEN";
+ tag = CMDTAG_LISTEN;
break;
case T_UnlistenStmt:
- tag = "UNLISTEN";
+ tag = CMDTAG_UNLISTEN;
break;
case T_LoadStmt:
- tag = "LOAD";
+ tag = CMDTAG_LOAD;
break;
case T_CallStmt:
- tag = "CALL";
+ tag = CMDTAG_CALL;
break;
case T_ClusterStmt:
- tag = "CLUSTER";
+ tag = CMDTAG_CLUSTER;
break;
case T_VacuumStmt:
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
- tag = "VACUUM";
+ tag = CMDTAG_VACUUM;
else
- tag = "ANALYZE";
+ tag = CMDTAG_ANALYZE;
break;
case T_ExplainStmt:
- tag = "EXPLAIN";
+ tag = CMDTAG_EXPLAIN;
break;
case T_CreateTableAsStmt:
@@ -2762,24 +2752,24 @@ CreateCommandTag(Node *parsetree)
{
case OBJECT_TABLE:
if (((CreateTableAsStmt *) parsetree)->is_select_into)
- tag = "SELECT INTO";
+ tag = CMDTAG_SELECT_INTO;
else
- tag = "CREATE TABLE AS";
+ tag = CMDTAG_CREATE_TABLE_AS;
break;
case OBJECT_MATVIEW:
- tag = "CREATE MATERIALIZED VIEW";
+ tag = CMDTAG_CREATE_MATERIALIZED_VIEW;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
case T_RefreshMatViewStmt:
- tag = "REFRESH MATERIALIZED VIEW";
+ tag = CMDTAG_REFRESH_MATERIALIZED_VIEW;
break;
case T_AlterSystemStmt:
- tag = "ALTER SYSTEM";
+ tag = CMDTAG_ALTER_SYSTEM;
break;
case T_VariableSetStmt:
@@ -2789,183 +2779,183 @@ CreateCommandTag(Node *parsetree)
case VAR_SET_CURRENT:
case VAR_SET_DEFAULT:
case VAR_SET_MULTI:
- tag = "SET";
+ tag = CMDTAG_SET;
break;
case VAR_RESET:
case VAR_RESET_ALL:
- tag = "RESET";
+ tag = CMDTAG_RESET;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
case T_VariableShowStmt:
- tag = "SHOW";
+ tag = CMDTAG_SHOW;
break;
case T_DiscardStmt:
switch (((DiscardStmt *) parsetree)->target)
{
case DISCARD_ALL:
- tag = "DISCARD ALL";
+ tag = CMDTAG_DISCARD_ALL;
break;
case DISCARD_PLANS:
- tag = "DISCARD PLANS";
+ tag = CMDTAG_DISCARD_PLANS;
break;
case DISCARD_TEMP:
- tag = "DISCARD TEMP";
+ tag = CMDTAG_DISCARD_TEMP;
break;
case DISCARD_SEQUENCES:
- tag = "DISCARD SEQUENCES";
+ tag = CMDTAG_DISCARD_SEQUENCES;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
case T_CreateTransformStmt:
- tag = "CREATE TRANSFORM";
+ tag = CMDTAG_CREATE_TRANSFORM;
break;
case T_CreateTrigStmt:
- tag = "CREATE TRIGGER";
+ tag = CMDTAG_CREATE_TRIGGER;
break;
case T_CreateEventTrigStmt:
- tag = "CREATE EVENT TRIGGER";
+ tag = CMDTAG_CREATE_EVENT_TRIGGER;
break;
case T_AlterEventTrigStmt:
- tag = "ALTER EVENT TRIGGER";
+ tag = CMDTAG_ALTER_EVENT_TRIGGER;
break;
case T_CreatePLangStmt:
- tag = "CREATE LANGUAGE";
+ tag = CMDTAG_CREATE_LANGUAGE;
break;
case T_CreateRoleStmt:
- tag = "CREATE ROLE";
+ tag = CMDTAG_CREATE_ROLE;
break;
case T_AlterRoleStmt:
- tag = "ALTER ROLE";
+ tag = CMDTAG_ALTER_ROLE;
break;
case T_AlterRoleSetStmt:
- tag = "ALTER ROLE";
+ tag = CMDTAG_ALTER_ROLE;
break;
case T_DropRoleStmt:
- tag = "DROP ROLE";
+ tag = CMDTAG_DROP_ROLE;
break;
case T_DropOwnedStmt:
- tag = "DROP OWNED";
+ tag = CMDTAG_DROP_OWNED;
break;
case T_ReassignOwnedStmt:
- tag = "REASSIGN OWNED";
+ tag = CMDTAG_REASSIGN_OWNED;
break;
case T_LockStmt:
- tag = "LOCK TABLE";
+ tag = CMDTAG_LOCK_TABLE;
break;
case T_ConstraintsSetStmt:
- tag = "SET CONSTRAINTS";
+ tag = CMDTAG_SET_CONSTRAINTS;
break;
case T_CheckPointStmt:
- tag = "CHECKPOINT";
+ tag = CMDTAG_CHECKPOINT;
break;
case T_ReindexStmt:
- tag = "REINDEX";
+ tag = CMDTAG_REINDEX;
break;
case T_CreateConversionStmt:
- tag = "CREATE CONVERSION";
+ tag = CMDTAG_CREATE_CONVERSION;
break;
case T_CreateCastStmt:
- tag = "CREATE CAST";
+ tag = CMDTAG_CREATE_CAST;
break;
case T_CreateOpClassStmt:
- tag = "CREATE OPERATOR CLASS";
+ tag = CMDTAG_CREATE_OPERATOR_CLASS;
break;
case T_CreateOpFamilyStmt:
- tag = "CREATE OPERATOR FAMILY";
+ tag = CMDTAG_CREATE_OPERATOR_FAMILY;
break;
case T_AlterOpFamilyStmt:
- tag = "ALTER OPERATOR FAMILY";
+ tag = CMDTAG_ALTER_OPERATOR_FAMILY;
break;
case T_AlterOperatorStmt:
- tag = "ALTER OPERATOR";
+ tag = CMDTAG_ALTER_OPERATOR;
break;
case T_AlterTSDictionaryStmt:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case T_AlterTSConfigurationStmt:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case T_CreatePolicyStmt:
- tag = "CREATE POLICY";
+ tag = CMDTAG_CREATE_POLICY;
break;
case T_AlterPolicyStmt:
- tag = "ALTER POLICY";
+ tag = CMDTAG_ALTER_POLICY;
break;
case T_CreateAmStmt:
- tag = "CREATE ACCESS METHOD";
+ tag = CMDTAG_CREATE_ACCESS_METHOD;
break;
case T_CreatePublicationStmt:
- tag = "CREATE PUBLICATION";
+ tag = CMDTAG_CREATE_PUBLICATION;
break;
case T_AlterPublicationStmt:
- tag = "ALTER PUBLICATION";
+ tag = CMDTAG_ALTER_PUBLICATION;
break;
case T_CreateSubscriptionStmt:
- tag = "CREATE SUBSCRIPTION";
+ tag = CMDTAG_CREATE_SUBSCRIPTION;
break;
case T_AlterSubscriptionStmt:
- tag = "ALTER SUBSCRIPTION";
+ tag = CMDTAG_ALTER_SUBSCRIPTION;
break;
case T_DropSubscriptionStmt:
- tag = "DROP SUBSCRIPTION";
+ tag = CMDTAG_DROP_SUBSCRIPTION;
break;
case T_AlterCollationStmt:
- tag = "ALTER COLLATION";
+ tag = CMDTAG_ALTER_COLLATION;
break;
case T_PrepareStmt:
- tag = "PREPARE";
+ tag = CMDTAG_PREPARE;
break;
case T_ExecuteStmt:
- tag = "EXECUTE";
+ tag = CMDTAG_EXECUTE;
break;
case T_CreateStatsStmt:
- tag = "CREATE STATISTICS";
+ tag = CMDTAG_CREATE_STATISTICS;
break;
case T_AlterStatsStmt:
- tag = "ALTER STATISTICS";
+ tag = CMDTAG_ALTER_STATISTICS;
break;
case T_DeallocateStmt:
@@ -2973,9 +2963,9 @@ CreateCommandTag(Node *parsetree)
DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
if (stmt->name == NULL)
- tag = "DEALLOCATE ALL";
+ tag = CMDTAG_DEALLOCATE_ALL;
else
- tag = "DEALLOCATE";
+ tag = CMDTAG_DEALLOCATE;
}
break;
@@ -2999,33 +2989,33 @@ CreateCommandTag(Node *parsetree)
switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = CMDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = CMDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = CMDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = CMDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "SELECT";
+ tag = CMDTAG_SELECT;
break;
}
}
else
- tag = "SELECT";
+ tag = CMDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = CMDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = CMDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = CMDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3033,7 +3023,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
}
@@ -3059,33 +3049,33 @@ CreateCommandTag(Node *parsetree)
switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = CMDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = CMDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = CMDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = CMDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
}
else
- tag = "SELECT";
+ tag = CMDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = CMDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = CMDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = CMDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3093,7 +3083,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
}
@@ -3102,7 +3092,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 1b63048a77..b9c1a0a5ad 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -20,6 +20,7 @@
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
+#include "tcop/cmdtag.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
@@ -51,7 +52,7 @@ static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD;
static void BuildEventTriggerCache(void);
static void InvalidateEventCacheCallback(Datum arg,
int cacheid, uint32 hashvalue);
-static int DecodeTextArrayToCString(Datum array, char ***cstringp);
+static Bitmapset *DecodeTextArrayToBitmapset(Datum array);
/*
* Search the event cache by trigger event.
@@ -180,10 +181,7 @@ BuildEventTriggerCache(void)
evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags,
RelationGetDescr(rel), &evttags_isnull);
if (!evttags_isnull)
- {
- item->ntags = DecodeTextArrayToCString(evttags, &item->tag);
- qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp);
- }
+ item->tagset = DecodeTextArrayToBitmapset(evttags);
/* Add to cache entry. */
entry = hash_search(cache, &event, HASH_ENTER, &found);
@@ -215,18 +213,18 @@ BuildEventTriggerCache(void)
}
/*
- * Decode text[] to an array of C strings.
+ * Decode text[] to a Bitmapset of CommandTags.
*
* We could avoid a bit of overhead here if we were willing to duplicate some
* of the logic from deconstruct_array, but it doesn't seem worth the code
* complexity.
*/
-static int
-DecodeTextArrayToCString(Datum array, char ***cstringp)
+static Bitmapset *
+DecodeTextArrayToBitmapset(Datum array)
{
ArrayType *arr = DatumGetArrayTypeP(array);
Datum *elems;
- char **cstring;
+ Bitmapset *bms;
int i;
int nelems;
@@ -234,13 +232,17 @@ DecodeTextArrayToCString(Datum array, char ***cstringp)
elog(ERROR, "expected 1-D text array");
deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems);
- cstring = palloc(nelems * sizeof(char *));
- for (i = 0; i < nelems; ++i)
- cstring[i] = TextDatumGetCString(elems[i]);
+ for (bms = NULL, i = 0; i < nelems; ++i)
+ {
+ char *str = TextDatumGetCString(elems[i]);
+
+ bms = bms_add_member(bms, GetCommandTagEnum(str));
+ pfree(str);
+ }
pfree(elems);
- *cstringp = cstring;
- return nelems;
+
+ return bms;
}
/*
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index c47be0ba4c..53401bd4e9 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -163,7 +163,7 @@ InitPlanCache(void)
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
MemoryContext source_context;
@@ -246,7 +246,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
CachedPlanSource *
CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index b675575c31..7072ce48a3 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -281,7 +281,7 @@ void
PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan)
{
@@ -289,10 +289,12 @@ PortalDefineQuery(Portal portal,
AssertState(portal->status == PORTAL_NEW);
AssertArg(sourceText != NULL);
- AssertArg(commandTag != NULL || stmts == NIL);
+ AssertArg(commandTag != CMDTAG_UNKNOWN || stmts == NIL);
portal->prepStmtName = prepStmtName;
portal->sourceText = sourceText;
+ portal->qc.commandTag = commandTag;
+ portal->qc.nprocessed = 0;
portal->commandTag = commandTag;
portal->stmts = stmts;
portal->cplan = cplan;
diff --git a/src/include/commands/createas.h b/src/include/commands/createas.h
index 7743851a38..5615b5ecac 100644
--- a/src/include/commands/createas.h
+++ b/src/include/commands/createas.h
@@ -22,7 +22,8 @@
extern ObjectAddress ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
- ParamListInfo params, QueryEnvironment *queryEnv, char *completionTag);
+ ParamListInfo params, QueryEnvironment *queryEnv,
+ QueryCompletion *qc);
extern int GetIntoRelEFlags(IntoClause *intoClause);
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index faa2958b89..28b352051b 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -17,6 +17,7 @@
#include "catalog/objectaddress.h"
#include "catalog/pg_event_trigger.h"
#include "nodes/parsenodes.h"
+#include "tcop/cmdtag.h"
#include "tcop/deparse_utility.h"
#include "utils/aclchk_internal.h"
@@ -25,7 +26,7 @@ typedef struct EventTriggerData
NodeTag type;
const char *event; /* event name */
Node *parsetree; /* parse tree */
- const char *tag; /* command tag */
+ CommandTag tag;
} EventTriggerData;
#define AT_REWRITE_ALTER_PERSISTENCE 0x01
diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h
index 6bdb7ca258..3ea4f5c80b 100644
--- a/src/include/commands/matview.h
+++ b/src/include/commands/matview.h
@@ -24,7 +24,7 @@
extern void SetMatViewPopulatedState(Relation relation, bool newstate);
extern ObjectAddress ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag);
+ ParamListInfo params, QueryCompletion *qc);
extern DestReceiver *CreateTransientRelDestReceiver(Oid oid);
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index 4ecc1a2ecd..5f64b0a674 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -23,7 +23,7 @@ extern void PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, Para
bool isTopLevel);
extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
- char *completionTag);
+ QueryCompletion *qc);
extern void PerformPortalClose(const char *name);
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index a0509e1f33..4fcf2406c1 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -40,7 +40,7 @@ extern void PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
extern void ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void DeallocateQuery(DeallocateStmt *stmt);
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into,
ExplainState *es, const char *queryString,
diff --git a/src/include/tcop/cmdtag.h b/src/include/tcop/cmdtag.h
new file mode 100644
index 0000000000..fd70349976
--- /dev/null
+++ b/src/include/tcop/cmdtag.h
@@ -0,0 +1,57 @@
+/*-------------------------------------------------------------------------
+ *
+ * cmdtag.h
+ * Declarations for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/tcop/cmdtag.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CMDTAG_H
+#define CMDTAG_H
+
+
+#define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt, lastoid) \
+ tag,
+
+typedef enum CommandTag
+{
+#include "tcop/cmdtaglist.h"
+ COMMAND_TAG_NEXTTAG
+} CommandTag;
+
+#undef PG_CMDTAG
+
+typedef struct QueryCompletion
+{
+ CommandTag commandTag;
+ uint64 nprocessed;
+} QueryCompletion;
+
+static inline void
+SetQueryCompletion(QueryCompletion *qc, CommandTag commandTag,
+ uint64 nprocessed)
+{
+ qc->commandTag = commandTag;
+ qc->nprocessed = nprocessed;
+}
+
+static inline void
+CopyQueryCompletion(QueryCompletion *dst, const QueryCompletion *src)
+{
+ dst->commandTag = src->commandTag;
+ dst->nprocessed = src->nprocessed;
+}
+
+extern void InitializeQueryCompletion(QueryCompletion *qc);
+extern const char *GetCommandTagName(CommandTag commandTag);
+extern bool command_tag_display_last_oid(CommandTag commandTag);
+extern bool command_tag_display_rowcount(CommandTag commandTag);
+extern bool command_tag_event_trigger_ok(CommandTag commandTag);
+extern bool command_tag_table_rewrite_ok(CommandTag commandTag);
+extern CommandTag GetCommandTagEnum(const char *tagname);
+
+#endif /* CMDTAG_H */
diff --git a/src/include/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h
new file mode 100644
index 0000000000..cf67a2298f
--- /dev/null
+++ b/src/include/tcop/cmdtaglist.h
@@ -0,0 +1,208 @@
+/*----------------------------------------------------------------------
+ *
+ * cmdtaglist.h
+ * Command tags
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/tcop/cmdtaglist.h
+ *
+ *----------------------------------------------------------------------
+ */
+
+/* there is deliberately not an #ifndef CMDTAGLIST_H here */
+
+/* symbol name, textual name, event_trigger_ok, table_rewrite_ok, rowcount, last_oid */
+PG_CMDTAG(CMDTAG_ALTER_ACCESS_METHOD, "ALTER ACCESS METHOD", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_AGGREGATE, "ALTER AGGREGATE", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_CAST, "ALTER CAST", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_COLLATION, "ALTER COLLATION", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_CONSTRAINT, "ALTER CONSTRAINT", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_CONVERSION, "ALTER CONVERSION", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_DATABASE, "ALTER DATABASE", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_DEFAULT_PRIVILEGES, "ALTER DEFAULT PRIVILEGES", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_DOMAIN, "ALTER DOMAIN", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_EVENT_TRIGGER, "ALTER EVENT TRIGGER", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_EXTENSION, "ALTER EXTENSION", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_FOREIGN_DATA_WRAPPER, "ALTER FOREIGN DATA WRAPPER", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_FOREIGN_TABLE, "ALTER FOREIGN TABLE", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_FUNCTION, "ALTER FUNCTION", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_INDEX, "ALTER INDEX", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_LANGUAGE, "ALTER LANGUAGE", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_LARGE_OBJECT, "ALTER LARGE OBJECT", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_MATERIALIZED_VIEW, "ALTER MATERIALIZED VIEW", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_OPERATOR, "ALTER OPERATOR", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_OPERATOR_CLASS, "ALTER OPERATOR CLASS", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_OPERATOR_FAMILY, "ALTER OPERATOR FAMILY", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_POLICY, "ALTER POLICY", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_PROCEDURE, "ALTER PROCEDURE", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_PUBLICATION, "ALTER PUBLICATION", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_ROLE, "ALTER ROLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_ROUTINE, "ALTER ROUTINE", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_RULE, "ALTER RULE", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SCHEMA, "ALTER SCHEMA", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SEQUENCE, "ALTER SEQUENCE", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SERVER, "ALTER SERVER", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_STATISTICS, "ALTER STATISTICS", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SUBSCRIPTION, "ALTER SUBSCRIPTION", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SYSTEM, "ALTER SYSTEM", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TABLE, "ALTER TABLE", true, true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TABLESPACE, "ALTER TABLESPACE", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_CONFIGURATION, "ALTER TEXT SEARCH CONFIGURATION", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_DICTIONARY, "ALTER TEXT SEARCH DICTIONARY", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_PARSER, "ALTER TEXT SEARCH PARSER", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_TEMPLATE, "ALTER TEXT SEARCH TEMPLATE", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TRANSFORM, "ALTER TRANSFORM", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TRIGGER, "ALTER TRIGGER", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TYPE, "ALTER TYPE", true, true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_USER_MAPPING, "ALTER USER MAPPING", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_VIEW, "ALTER VIEW", true, false, false, false)
+PG_CMDTAG(CMDTAG_ANALYZE, "ANALYZE", false, false, false, false)
+PG_CMDTAG(CMDTAG_BEGIN, "BEGIN", false, false, false, false)
+PG_CMDTAG(CMDTAG_CALL, "CALL", false, false, false, false)
+PG_CMDTAG(CMDTAG_CHECKPOINT, "CHECKPOINT", false, false, false, false)
+PG_CMDTAG(CMDTAG_CLOSE, "CLOSE", false, false, false, false)
+PG_CMDTAG(CMDTAG_CLOSE_CURSOR, "CLOSE CURSOR", false, false, false, false)
+PG_CMDTAG(CMDTAG_CLOSE_CURSOR_ALL, "CLOSE CURSOR ALL", false, false, false, false)
+PG_CMDTAG(CMDTAG_CLUSTER, "CLUSTER", false, false, false, false)
+PG_CMDTAG(CMDTAG_COMMENT, "COMMENT", true, false, false, false)
+PG_CMDTAG(CMDTAG_COMMIT, "COMMIT", false, false, false, false)
+PG_CMDTAG(CMDTAG_COMMIT_PREPARED, "COMMIT PREPARED", false, false, false, false)
+PG_CMDTAG(CMDTAG_COPY, "COPY", false, false, true, false)
+PG_CMDTAG(CMDTAG_COPY_FROM, "COPY FROM", false, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_ACCESS_METHOD, "CREATE ACCESS METHOD", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_AGGREGATE, "CREATE AGGREGATE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_CAST, "CREATE CAST", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_COLLATION, "CREATE COLLATION", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_CONSTRAINT, "CREATE CONSTRAINT", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_CONVERSION, "CREATE CONVERSION", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_DATABASE, "CREATE DATABASE", false, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_DOMAIN, "CREATE DOMAIN", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_EVENT_TRIGGER, "CREATE EVENT TRIGGER", false, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_EXTENSION, "CREATE EXTENSION", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_FOREIGN_DATA_WRAPPER, "CREATE FOREIGN DATA WRAPPER", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_FOREIGN_TABLE, "CREATE FOREIGN TABLE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_FUNCTION, "CREATE FUNCTION", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_INDEX, "CREATE INDEX", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_LANGUAGE, "CREATE LANGUAGE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_MATERIALIZED_VIEW, "CREATE MATERIALIZED VIEW", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_OPERATOR, "CREATE OPERATOR", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_OPERATOR_CLASS, "CREATE OPERATOR CLASS", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_OPERATOR_FAMILY, "CREATE OPERATOR FAMILY", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_POLICY, "CREATE POLICY", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_PROCEDURE, "CREATE PROCEDURE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_PUBLICATION, "CREATE PUBLICATION", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_ROLE, "CREATE ROLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_ROUTINE, "CREATE ROUTINE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_RULE, "CREATE RULE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_SCHEMA, "CREATE SCHEMA", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_SEQUENCE, "CREATE SEQUENCE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_SERVER, "CREATE SERVER", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_STATISTICS, "CREATE STATISTICS", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_SUBSCRIPTION, "CREATE SUBSCRIPTION", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TABLE, "CREATE TABLE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TABLE_AS, "CREATE TABLE AS", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TABLESPACE, "CREATE TABLESPACE", false, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_CONFIGURATION, "CREATE TEXT SEARCH CONFIGURATION", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_DICTIONARY, "CREATE TEXT SEARCH DICTIONARY", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_PARSER, "CREATE TEXT SEARCH PARSER", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_TEMPLATE, "CREATE TEXT SEARCH TEMPLATE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TRANSFORM, "CREATE TRANSFORM", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TRIGGER, "CREATE TRIGGER", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TYPE, "CREATE TYPE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_USER_MAPPING, "CREATE USER MAPPING", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_VIEW, "CREATE VIEW", true, false, false, false)
+PG_CMDTAG(CMDTAG_DEALLOCATE, "DEALLOCATE", false, false, false, false)
+PG_CMDTAG(CMDTAG_DEALLOCATE_ALL, "DEALLOCATE ALL", false, false, false, false)
+PG_CMDTAG(CMDTAG_DECLARE_CURSOR, "DECLARE CURSOR", false, false, false, false)
+PG_CMDTAG(CMDTAG_DELETE, "DELETE", false, false, true, false)
+PG_CMDTAG(CMDTAG_DISCARD, "DISCARD", false, false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_ALL, "DISCARD ALL", false, false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_PLANS, "DISCARD PLANS", false, false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_SEQUENCES, "DISCARD SEQUENCES", false, false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_TEMP, "DISCARD TEMP", false, false, false, false)
+PG_CMDTAG(CMDTAG_DO, "DO", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_ACCESS_METHOD, "DROP ACCESS METHOD", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_AGGREGATE, "DROP AGGREGATE", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_CAST, "DROP CAST", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_COLLATION, "DROP COLLATION", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_CONSTRAINT, "DROP CONSTRAINT", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_CONVERSION, "DROP CONVERSION", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_DATABASE, "DROP DATABASE", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_DOMAIN, "DROP DOMAIN", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_EVENT_TRIGGER, "DROP EVENT TRIGGER", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_EXTENSION, "DROP EXTENSION", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_FOREIGN_DATA_WRAPPER, "DROP FOREIGN DATA WRAPPER", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_FOREIGN_TABLE, "DROP FOREIGN TABLE", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_FUNCTION, "DROP FUNCTION", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_INDEX, "DROP INDEX", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_LANGUAGE, "DROP LANGUAGE", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_MATERIALIZED_VIEW, "DROP MATERIALIZED VIEW", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_OPERATOR, "DROP OPERATOR", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_OPERATOR_CLASS, "DROP OPERATOR CLASS", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_OPERATOR_FAMILY, "DROP OPERATOR FAMILY", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_OWNED, "DROP OWNED", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_POLICY, "DROP POLICY", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_PROCEDURE, "DROP PROCEDURE", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_PUBLICATION, "DROP PUBLICATION", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_REPLICATION_SLOT, "DROP REPLICATION SLOT", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_ROLE, "DROP ROLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_ROUTINE, "DROP ROUTINE", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_RULE, "DROP RULE", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_SCHEMA, "DROP SCHEMA", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_SEQUENCE, "DROP SEQUENCE", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_SERVER, "DROP SERVER", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_STATISTICS, "DROP STATISTICS", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_SUBSCRIPTION, "DROP SUBSCRIPTION", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TABLE, "DROP TABLE", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TABLESPACE, "DROP TABLESPACE", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_CONFIGURATION, "DROP TEXT SEARCH CONFIGURATION", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_DICTIONARY, "DROP TEXT SEARCH DICTIONARY", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_PARSER, "DROP TEXT SEARCH PARSER", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_TEMPLATE, "DROP TEXT SEARCH TEMPLATE", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TRANSFORM, "DROP TRANSFORM", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TRIGGER, "DROP TRIGGER", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TYPE, "DROP TYPE", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_USER_MAPPING, "DROP USER MAPPING", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_VIEW, "DROP VIEW", true, false, false, false)
+PG_CMDTAG(CMDTAG_EXECUTE, "EXECUTE", false, false, false, false)
+PG_CMDTAG(CMDTAG_EXPLAIN, "EXPLAIN", false, false, false, false)
+PG_CMDTAG(CMDTAG_FETCH, "FETCH", false, false, true, false)
+PG_CMDTAG(CMDTAG_GRANT, "GRANT", true, false, false, false)
+PG_CMDTAG(CMDTAG_GRANT_ROLE, "GRANT ROLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_IMPORT_FOREIGN_SCHEMA, "IMPORT FOREIGN SCHEMA", true, false, false, false)
+PG_CMDTAG(CMDTAG_INSERT, "INSERT", false, false, true, true)
+PG_CMDTAG(CMDTAG_LISTEN, "LISTEN", false, false, false, false)
+PG_CMDTAG(CMDTAG_LOAD, "LOAD", false, false, false, false)
+PG_CMDTAG(CMDTAG_LOCK_TABLE, "LOCK TABLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_MOVE, "MOVE", false, false, true, false)
+PG_CMDTAG(CMDTAG_NOTIFY, "NOTIFY", false, false, false, false)
+PG_CMDTAG(CMDTAG_PREPARE, "PREPARE", false, false, false, false)
+PG_CMDTAG(CMDTAG_PREPARE_TRANSACTION, "PREPARE TRANSACTION", false, false, false, false)
+PG_CMDTAG(CMDTAG_REASSIGN_OWNED, "REASSIGN OWNED", false, false, false, false)
+PG_CMDTAG(CMDTAG_REFRESH_MATERIALIZED_VIEW, "REFRESH MATERIALIZED VIEW", true, false, false, false)
+PG_CMDTAG(CMDTAG_REINDEX, "REINDEX", false, false, false, false)
+PG_CMDTAG(CMDTAG_RELEASE, "RELEASE", false, false, false, false)
+PG_CMDTAG(CMDTAG_RESET, "RESET", false, false, false, false)
+PG_CMDTAG(CMDTAG_REVOKE, "REVOKE", true, false, false, false)
+PG_CMDTAG(CMDTAG_REVOKE_ROLE, "REVOKE ROLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_ROLLBACK, "ROLLBACK", false, false, false, false)
+PG_CMDTAG(CMDTAG_ROLLBACK_PREPARED, "ROLLBACK PREPARED", false, false, false, false)
+PG_CMDTAG(CMDTAG_SAVEPOINT, "SAVEPOINT", false, false, false, false)
+PG_CMDTAG(CMDTAG_SECURITY_LABEL, "SECURITY LABEL", true, false, false, false)
+PG_CMDTAG(CMDTAG_SELECT, "SELECT", false, false, true, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_KEY_SHARE, "SELECT FOR KEY SHARE", false, false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_NO_KEY_UPDATE, "SELECT FOR NO KEY UPDATE", false, false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_SHARE, "SELECT FOR SHARE", false, false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_UPDATE, "SELECT FOR UPDATE", false, false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_INTO, "SELECT INTO", true, false, false, false)
+PG_CMDTAG(CMDTAG_SET, "SET", false, false, false, false)
+PG_CMDTAG(CMDTAG_SET_CONSTRAINTS, "SET CONSTRAINTS", false, false, false, false)
+PG_CMDTAG(CMDTAG_SHOW, "SHOW", false, false, false, false)
+PG_CMDTAG(CMDTAG_START_TRANSACTION, "START TRANSACTION", false, false, false, false)
+PG_CMDTAG(CMDTAG_TRUNCATE_TABLE, "TRUNCATE TABLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_UNKNOWN, "???", false, false, false, false)
+PG_CMDTAG(CMDTAG_UNLISTEN, "UNLISTEN", false, false, false, false)
+PG_CMDTAG(CMDTAG_UPDATE, "UPDATE", false, false, true, false)
+PG_CMDTAG(CMDTAG_VACUUM, "VACUUM", false, false, false, false)
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 35bce731a1..662ce8a56f 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -68,6 +68,7 @@
#define DEST_H
#include "executor/tuptable.h"
+#include "tcop/cmdtag.h"
/* buffer size to use for command completion tags */
@@ -134,9 +135,10 @@ extern PGDLLIMPORT DestReceiver *None_Receiver; /* permanent receiver for
/* The primary destination management functions */
-extern void BeginCommand(const char *commandTag, CommandDest dest);
+extern void BeginCommand(CommandTag commandTag, CommandDest dest);
extern DestReceiver *CreateDestReceiver(CommandDest dest);
-extern void EndCommand(const char *commandTag, CommandDest dest);
+extern void EndCommand(const QueryCompletion *qc, CommandDest dest,
+ bool force_undecorated_output);
/* Additional functions that go with destination management, more or less. */
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index 4ad6324e2d..437642cc72 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.h
@@ -35,7 +35,7 @@ extern void PortalSetResultFormat(Portal portal, int nFormats,
extern bool PortalRun(Portal portal, long count, bool isTopLevel,
bool run_once, DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletion *qc);
extern uint64 PortalRunFetch(Portal portal,
FetchDirection fdirection,
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index a551e08cb8..4aec19a008 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -14,6 +14,7 @@
#ifndef UTILITY_H
#define UTILITY_H
+#include "tcop/cmdtag.h"
#include "tcop/tcopprot.h"
typedef enum
@@ -71,17 +72,17 @@ typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
const char *queryString, ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void ProcessUtilityForAlterTable(Node *stmt,
AlterTableUtilityContext *context);
@@ -92,7 +93,13 @@ extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
extern Query *UtilityContainsQuery(Node *parsetree);
-extern const char *CreateCommandTag(Node *parsetree);
+extern CommandTag CreateCommandTag(Node *parsetree);
+
+static inline const char *
+CreateCommandName(Node *parsetree)
+{
+ return GetCommandTagName(CreateCommandTag(parsetree));
+}
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index 6c3ff81ba3..bc8ce48061 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -28,8 +28,7 @@ typedef struct
{
Oid fnoid; /* function to be called */
char enabled; /* as SESSION_REPLICATION_ROLE_* */
- int ntags; /* number of command tags */
- char **tag; /* command tags in SORTED order */
+ Bitmapset *tagset; /* command tags, or NULL if empty */
} EventTriggerCacheItem;
extern List *EventCacheLookup(EventTriggerEvent event);
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index e48661ebec..6dde441586 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -18,6 +18,7 @@
#include "access/tupdesc.h"
#include "lib/ilist.h"
#include "nodes/params.h"
+#include "tcop/cmdtag.h"
#include "utils/queryenvironment.h"
/* Forward declaration, to avoid including parsenodes.h here */
@@ -95,7 +96,7 @@ typedef struct CachedPlanSource
int magic; /* should equal CACHEDPLANSOURCE_MAGIC */
struct RawStmt *raw_parse_tree; /* output of raw_parser(), or NULL */
const char *query_string; /* source text of query */
- const char *commandTag; /* command tag (a constant!), or NULL */
+ CommandTag commandTag;
Oid *param_types; /* array of parameter type OIDs, or NULL */
int num_params; /* length of param_types array */
ParserSetupHook parserSetup; /* alternative parameter spec method */
@@ -186,10 +187,10 @@ extern void ResetPlanCache(void);
extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern void CompleteCachedPlan(CachedPlanSource *plansource,
List *querytree_list,
MemoryContext querytree_context,
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 0b69433722..d41ff2efda 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -48,6 +48,7 @@
#include "datatype/timestamp.h"
#include "executor/execdesc.h"
+#include "tcop/cmdtag.h"
#include "utils/plancache.h"
#include "utils/resowner.h"
@@ -132,7 +133,8 @@ typedef struct PortalData
/* The query or queries the portal will execute */
const char *sourceText; /* text of query (as of 8.4, never NULL) */
- const char *commandTag; /* command tag for original query */
+ CommandTag commandTag; /* command tag for original query */
+ QueryCompletion qc; /* command completion data for executed query */
List *stmts; /* list of PlannedStmts */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
@@ -227,7 +229,7 @@ extern Portal GetPortalByName(const char *name);
extern void PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan);
extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index f5de2332d5..a65bce0713 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1737,7 +1737,7 @@ plperl_event_trigger_build_args(FunctionCallInfo fcinfo)
tdata = (EventTriggerData *) fcinfo->context;
hv_store_string(hv, "event", cstr2sv(tdata->event));
- hv_store_string(hv, "tag", cstr2sv(tdata->tag));
+ hv_store_string(hv, "tag", cstr2sv(GetCommandTagName(tdata->tag)));
return newRV_noinc((SV *) hv);
}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 5acf604f63..a867c2c43b 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -37,6 +37,7 @@
#include "parser/scansup.h"
#include "plpgsql.h"
#include "storage/proc.h"
+#include "tcop/cmdtag.h"
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
#include "utils/array.h"
@@ -1473,7 +1474,7 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
case PLPGSQL_PROMISE_TG_TAG:
if (estate->evtrigdata == NULL)
elog(ERROR, "event trigger promise is not in an event trigger function");
- assign_text_var(estate, var, estate->evtrigdata->tag);
+ assign_text_var(estate, var, GetCommandTagName(estate->evtrigdata->tag));
break;
default:
@@ -4115,10 +4116,9 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
* tree(s), since those are the result of rewriting and could have
* been transmogrified into something else entirely.
*/
- if (plansource->commandTag &&
- (strcmp(plansource->commandTag, "INSERT") == 0 ||
- strcmp(plansource->commandTag, "UPDATE") == 0 ||
- strcmp(plansource->commandTag, "DELETE") == 0))
+ if (plansource->commandTag == CMDTAG_INSERT ||
+ plansource->commandTag == CMDTAG_UPDATE ||
+ plansource->commandTag == CMDTAG_DELETE)
{
stmt->mod_stmt = true;
break;
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index f0d170bec7..26e76f6a51 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -1329,7 +1329,8 @@ pltcl_event_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
Tcl_ListObjAppendElement(NULL, tcl_cmd,
Tcl_NewStringObj(utf_e2u(tdata->event), -1));
Tcl_ListObjAppendElement(NULL, tcl_cmd,
- Tcl_NewStringObj(utf_e2u(tdata->tag), -1));
+ Tcl_NewStringObj(utf_e2u(GetCommandTagName(tdata->tag)),
+ -1));
tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
diff --git a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
index e1629ec618..b7bdb88ce7 100644
--- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
+++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
@@ -74,7 +74,7 @@ get_command_tag(PG_FUNCTION_ARGS)
if (!cmd->parsetree)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(cstring_to_text(CreateCommandTag(cmd->parsetree)));
+ PG_RETURN_TEXT_P(cstring_to_text(CreateCommandName(cmd->parsetree)));
}
/*
--
2.20.1
I just realized that we could rename command_tag_display_last_oid() to
something like command_tag_print_a_useless_zero_for_historical_reasons()
and nothing would be lost.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
I just realized that we could rename command_tag_display_last_oid() to
something like command_tag_print_a_useless_zero_for_historical_reasons()
and nothing would be lost.
Is there a way to drop that logic altogether by making the tagname string
be "INSERT 0" for the INSERT case? Or would the zero bleed into other
places where we don't want it?
regards, tom lane
On 2020-Feb-28, Tom Lane wrote:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
I just realized that we could rename command_tag_display_last_oid() to
something like command_tag_print_a_useless_zero_for_historical_reasons()
and nothing would be lost.Is there a way to drop that logic altogether by making the tagname string
be "INSERT 0" for the INSERT case? Or would the zero bleed into other
places where we don't want it?
Hmm, interesting thought. But yeah, it would show up in ps display:
commandTag = CreateCommandTag(parsetree->stmt);
set_ps_display(GetCommandTagName(commandTag), false);
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Feb 28, 2020, at 3:05 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
I just realized that we could rename command_tag_display_last_oid() to
something like command_tag_print_a_useless_zero_for_historical_reasons()
and nothing would be lost.Is there a way to drop that logic altogether by making the tagname string
be "INSERT 0" for the INSERT case? Or would the zero bleed into other
places where we don't want it?
In general, I don't think we want to increase the number of distinct tags. Which command you finished running and whether you want a rowcount and/or lastoid are orthogonal issues. We already have problems with there being different commandtags for different versions of morally the same commands. Take for example "SELECT FOR KEY SHARE" vs. "SELECT FOR NO KEY UPDATE" vs. "SELECT FOR SHARE" vs. "SELECT FOR UPDATE".
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Mark Dilger <mark.dilger@enterprisedb.com> writes:
On Feb 28, 2020, at 3:05 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Is there a way to drop that logic altogether by making the tagname string
be "INSERT 0" for the INSERT case? Or would the zero bleed into other
places where we don't want it?
In general, I don't think we want to increase the number of distinct
tags. Which command you finished running and whether you want a
rowcount and/or lastoid are orthogonal issues.
Well, my thought is that last_oid is gone and it isn't ever coming back.
So the less code we use supporting a dead feature, the better.
If we can't remove the special case in EndCommand() altogether, I'd be
inclined to hard-code it as "if (tag == CMDTAG_INSERT ..." rather than
expend infrastructure on treating last_oid as a live option for commands
to have.
regards, tom lane
On Feb 28, 2020, at 5:42 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Mark Dilger <mark.dilger@enterprisedb.com> writes:
On Feb 28, 2020, at 3:05 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Is there a way to drop that logic altogether by making the tagname string
be "INSERT 0" for the INSERT case? Or would the zero bleed into other
places where we don't want it?In general, I don't think we want to increase the number of distinct
tags. Which command you finished running and whether you want a
rowcount and/or lastoid are orthogonal issues.Well, my thought is that last_oid is gone and it isn't ever coming back.
So the less code we use supporting a dead feature, the better.If we can't remove the special case in EndCommand() altogether, I'd be
inclined to hard-code it as "if (tag == CMDTAG_INSERT ..." rather than
expend infrastructure on treating last_oid as a live option for commands
to have.
You may want to think about the embedding of InvalidOid into the EndCommand output differently from how you think about the embedding of the rowcount into the EndCommand output, but my preference is to treat these issues the same and make a strong distinction between the commandtag and the embedded oid and/or rowcount. It's hard to say how many future features would be crippled by having the embedded InvalidOid in the commandtag, but as an example *right now* in the works, we have a feature to count how many commands of a given type have been executed. It stands to reason that feature, whether accepted in its current form or refactored, would not want to show users a pg_stats table like this:
cnt command
---- -------------
5 INSERT 0
37 SELECT
What the heck is the zero doing after the INSERT? That's the hardcoded InvalidOid that you are apparently arguing for. You could get around that by having the pg_sql_stats patch have its own separate set of command tag strings, but why would we intentionally design that sort of duplication into the solution?
As for hardcoding the behavior of whether to embed a rowcount in the output from EndCommand; In src/backend/replication/walsender.c, exec_replication_command() returns "SELECT" from EndCommand, and not "SELECT $rowcount" like everywhere else. The patch as submitted does not change behavior. It only refactors the code while preserving the current behavior. So we would have to agree that the patch can change how exec_replication_command() behaves and start embedding a rowcount there, too, if we want to make SELECT behave the same everywhere.
There is another problem, though, which is that if we're hoping to eventually abate this historical behavior and stop embedding InvalidOid and/or rowcount in the commandtag returned from EndCommand, it might be necessary (for backward compatibility with clients) to do that incrementally, in which case we still need the distinction between commandtags and formats to exist in the code. How else can you say that, for example, in the next rev of the protocol that we're not going to embed InvalidOid anymore, but we will continue to return it for clients who connect via the older protocol? What if the next rev of the protocol still returns rowcount, but in a way that doesn't require the clients to implement (or link to) a parser that extracts the rowcount by parsing a string?
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 2020-Feb-29, Mark Dilger wrote:
You may want to think about the embedding of InvalidOid into the EndCommand output differently from how you think about the embedding of the rowcount into the EndCommand output, but my preference is to treat these issues the same and make a strong distinction between the commandtag and the embedded oid and/or rowcount. It's hard to say how many future features would be crippled by having the embedded InvalidOid in the commandtag, but as an example *right now* in the works, we have a feature to count how many commands of a given type have been executed. It stands to reason that feature, whether accepted in its current form or refactored, would not want to show users a pg_stats table like this:
cnt command
---- -------------
5 INSERT 0
37 SELECTWhat the heck is the zero doing after the INSERT? That's the hardcoded InvalidOid that you are apparently arguing for. You could get around that by having the pg_sql_stats patch have its own separate set of command tag strings, but why would we intentionally design that sort of duplication into the solution?
This is what I think Tom means to use in EndCommand:
if (command_tag_display_rowcount(tag) && !force_undecorated_output)
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
tag == CMDTAG_INSERT ?
"%s 0 " UINT64_FORMAT : "%s " UINT64_FORMAT,
tagname, qc->nprocessed);
else
... no rowcount ...
The point is not to change the returned tag in any way -- just to make
the method to arrive at it not involve the additional data column in the
data file, instead hardcode the behavior in EndCommand. I don't
understand your point of pg_stats_sql having to deal with this in a
particular way. How is that patch obtaining the command tags? I would
hope it calls GetCommandTagName() rather than call CommandEnd, but maybe
I misunderstand where it hooks.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Mar 2, 2020, at 8:12 AM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
On 2020-Feb-29, Mark Dilger wrote:
You may want to think about the embedding of InvalidOid into the EndCommand output differently from how you think about the embedding of the rowcount into the EndCommand output, but my preference is to treat these issues the same and make a strong distinction between the commandtag and the embedded oid and/or rowcount. It's hard to say how many future features would be crippled by having the embedded InvalidOid in the commandtag, but as an example *right now* in the works, we have a feature to count how many commands of a given type have been executed. It stands to reason that feature, whether accepted in its current form or refactored, would not want to show users a pg_stats table like this:
cnt command
---- -------------
5 INSERT 0
37 SELECTWhat the heck is the zero doing after the INSERT? That's the hardcoded InvalidOid that you are apparently arguing for. You could get around that by having the pg_sql_stats patch have its own separate set of command tag strings, but why would we intentionally design that sort of duplication into the solution?
This is what I think Tom means to use in EndCommand:
if (command_tag_display_rowcount(tag) && !force_undecorated_output)
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
tag == CMDTAG_INSERT ?
"%s 0 " UINT64_FORMAT : "%s " UINT64_FORMAT,
tagname, qc->nprocessed);
else
... no rowcount ...The point is not to change the returned tag in any way -- just to make
the method to arrive at it not involve the additional data column in the
data file, instead hardcode the behavior in EndCommand.
Thanks, Álvaro, I think I get it now. I thought Tom was arguing to have "INSERT 0" rather than "INSERT" be the commandtag.
I don't
understand your point of pg_stats_sql having to deal with this in a
particular way. How is that patch obtaining the command tags? I would
hope it calls GetCommandTagName() rather than call CommandEnd, but maybe
I misunderstand where it hooks.
My objection was based on my misunderstanding of what Tom was requesting.
I can rework the patch the way Tom suggests.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 2020-Mar-02, Mark Dilger wrote:
I don't
understand your point of pg_stats_sql having to deal with this in a
particular way. How is that patch obtaining the command tags? I would
hope it calls GetCommandTagName() rather than call CommandEnd, but maybe
I misunderstand where it hooks.My objection was based on my misunderstanding of what Tom was requesting.
I can rework the patch the way Tom suggests.
I already did it :-) Posting in a jiffy
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Here's the patch I propose for commit. I also rewrote the commit
message.
There are further refinements that can be done, but they don't need to
be in the first patch. Notably, the event trigger code can surely do a
lot better now by translating the tag list to a bitmapset earlier.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
v7-0001-Migrating-commandTag-from-string-to-enum.patchtext/x-diff; charset=utf-8Download
From 5e5605ec913fb4ea89665debc57d430bbda34b8f Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 2 Mar 2020 13:39:16 -0300
Subject: [PATCH v7] Migrating commandTag from string to enum.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The backend was using strings to represent command tags and doing string
comparisons in multiple places, but that's slow and unhelpful. Create a
new command list with a supporting structure to use instead; this is
stored in a tag-list-file that can be tailored to specific purposes with
a caller-defineable C macro, similar to what we do for WAL resource
managers. The first first such uses are a new CommandTag enum and a
CommandTagBehavior struct.
Replace numerous occurrences of char *completionTag with a
QueryCompletion struct so that the code no longer stores information
about completed queries in a cstring. Only at the last moment, in
EndCommand(), does this get converted to a string.
EventTriggerCacheItem no longer holds an array of pallocâd tag strings
in sorted order, but rather just a Bitmapset over the CommandTags.
Author: Mark Dilger
Reviewed-by: John Naylor, Tom Lane, Ãlvaro Herrera
Discussion: https://postgr.es/m/981A9DB4-3F0C-4DA5-88AD-CB9CFF4D6CAD@enterprisedb.com
---
.../pg_stat_statements/pg_stat_statements.c | 18 +-
contrib/sepgsql/hooks.c | 6 +-
doc/src/sgml/event-trigger.sgml | 2 +-
src/backend/commands/createas.c | 14 +-
src/backend/commands/event_trigger.c | 160 +----
src/backend/commands/matview.c | 2 +-
src/backend/commands/portalcmds.c | 16 +-
src/backend/commands/prepare.c | 4 +-
src/backend/executor/execMain.c | 4 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/spi.c | 22 +-
src/backend/replication/logical/decode.c | 2 +-
src/backend/replication/walsender.c | 18 +-
src/backend/tcop/Makefile | 1 +
src/backend/tcop/cmdtag.c | 98 +++
src/backend/tcop/dest.c | 32 +-
src/backend/tcop/postgres.c | 24 +-
src/backend/tcop/pquery.c | 112 ++--
src/backend/tcop/utility.c | 560 +++++++++---------
src/backend/utils/cache/evtcache.c | 30 +-
src/backend/utils/cache/plancache.c | 4 +-
src/backend/utils/mmgr/portalmem.c | 6 +-
src/include/commands/createas.h | 3 +-
src/include/commands/event_trigger.h | 3 +-
src/include/commands/matview.h | 2 +-
src/include/commands/portalcmds.h | 2 +-
src/include/commands/prepare.h | 2 +-
src/include/tcop/cmdtag.h | 56 ++
src/include/tcop/cmdtaglist.h | 218 +++++++
src/include/tcop/dest.h | 6 +-
src/include/tcop/pquery.h | 2 +-
src/include/tcop/utility.h | 15 +-
src/include/utils/evtcache.h | 3 +-
src/include/utils/plancache.h | 7 +-
src/include/utils/portal.h | 6 +-
src/pl/plperl/plperl.c | 2 +-
src/pl/plpgsql/src/pl_exec.c | 10 +-
src/pl/tcl/pltcl.c | 3 +-
.../test_ddl_deparse/test_ddl_deparse.c | 2 +-
39 files changed, 873 insertions(+), 608 deletions(-)
create mode 100644 src/backend/tcop/cmdtag.c
create mode 100644 src/include/tcop/cmdtag.h
create mode 100644 src/include/tcop/cmdtaglist.h
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index e4fda4b404..7b8e690c95 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -307,7 +307,7 @@ static void pgss_ExecutorEnd(QueryDesc *queryDesc);
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
static uint64 pgss_hash_string(const char *str, int len);
static void pgss_store(const char *query, uint64 queryId,
int query_location, int query_len,
@@ -960,7 +960,7 @@ static void
pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context,
ParamListInfo params, QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
@@ -998,11 +998,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
@@ -1013,10 +1013,8 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
INSTR_TIME_SET_CURRENT(duration);
INSTR_TIME_SUBTRACT(duration, start);
- /* parse command tag to retrieve the number of affected rows. */
- if (completionTag &&
- strncmp(completionTag, "COPY ", 5) == 0)
- rows = pg_strtouint64(completionTag + 5, NULL, 10);
+ if (qc && qc->commandTag == CMDTAG_COPY)
+ rows = qc->nprocessed;
else
rows = 0;
@@ -1060,11 +1058,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
}
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 997a64c87e..853b5b04ab 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -317,7 +317,7 @@ sepgsql_utility_command(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
@@ -380,11 +380,11 @@ sepgsql_utility_command(PlannedStmt *pstmt,
if (next_ProcessUtility_hook)
(*next_ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
PG_FINALLY();
{
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index 18628c498b..130f6cd886 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -1074,7 +1074,7 @@ typedef struct EventTriggerData
NodeTag type;
const char *event; /* event name */
Node *parsetree; /* parse tree */
- const char *tag; /* command tag */
+ CommandTag tag; /* command tag */
} EventTriggerData;
</programlisting>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cc02cf824e..3a5676fb39 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -10,7 +10,7 @@
*
* Formerly, CTAS was implemented as a variant of SELECT, which led
* to assorted legacy behaviors that we still try to preserve, notably that
- * we must return a tuples-processed count in the completionTag. (We no
+ * we must return a tuples-processed count in the QueryCompletion. (We no
* longer do that for CTAS ... WITH NO DATA, however.)
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
@@ -225,7 +225,7 @@ create_ctas_nodata(List *tlist, IntoClause *into)
ObjectAddress
ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ParamListInfo params, QueryEnvironment *queryEnv,
- char *completionTag)
+ QueryCompletion *qc)
{
Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
@@ -270,7 +270,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
Assert(!is_matview); /* excluded by syntax */
- ExecuteQuery(pstate, estmt, into, params, dest, completionTag);
+ ExecuteQuery(pstate, estmt, into, params, dest, qc);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
@@ -352,11 +352,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* run the plan to completion */
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
- /* save the rowcount if we're given a completionTag to fill */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ /* save the rowcount if we're given a qc to fill */
+ if (qc)
+ SetQueryCompletion(qc, CMDTAG_SELECT, queryDesc->estate->es_processed);
/* get object address that intorel_startup saved for us */
address = ((DR_intorel *) dest)->reladdr;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 71911d4067..a366869369 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -78,59 +78,6 @@ typedef struct
bool supported;
} event_trigger_support_data;
-typedef enum
-{
- EVENT_TRIGGER_COMMAND_TAG_OK,
- EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED,
- EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED
-} event_trigger_command_tag_check_result;
-
-/* XXX merge this with ObjectTypeMap? */
-static const event_trigger_support_data event_trigger_support[] = {
- {"ACCESS METHOD", true},
- {"AGGREGATE", true},
- {"CAST", true},
- {"CONSTRAINT", true},
- {"COLLATION", true},
- {"CONVERSION", true},
- {"DATABASE", false},
- {"DOMAIN", true},
- {"EXTENSION", true},
- {"EVENT TRIGGER", false},
- {"FOREIGN DATA WRAPPER", true},
- {"FOREIGN TABLE", true},
- {"FUNCTION", true},
- {"INDEX", true},
- {"LANGUAGE", true},
- {"MATERIALIZED VIEW", true},
- {"OPERATOR", true},
- {"OPERATOR CLASS", true},
- {"OPERATOR FAMILY", true},
- {"POLICY", true},
- {"PROCEDURE", true},
- {"PUBLICATION", true},
- {"ROLE", false},
- {"ROUTINE", true},
- {"RULE", true},
- {"SCHEMA", true},
- {"SEQUENCE", true},
- {"SERVER", true},
- {"STATISTICS", true},
- {"SUBSCRIPTION", true},
- {"TABLE", true},
- {"TABLESPACE", false},
- {"TRANSFORM", true},
- {"TRIGGER", true},
- {"TEXT SEARCH CONFIGURATION", true},
- {"TEXT SEARCH DICTIONARY", true},
- {"TEXT SEARCH PARSER", true},
- {"TEXT SEARCH TEMPLATE", true},
- {"TYPE", true},
- {"USER MAPPING", true},
- {"VIEW", true},
- {NULL, false}
-};
-
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -150,8 +97,6 @@ typedef struct SQLDropObject
static void AlterEventTriggerOwner_internal(Relation rel,
HeapTuple tup,
Oid newOwnerId);
-static event_trigger_command_tag_check_result check_ddl_tag(const char *tag);
-static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(const char *tag);
static void error_duplicate_filter_variable(const char *defname);
static Datum filter_list_to_array(List *filterlist);
static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname,
@@ -259,71 +204,23 @@ validate_ddl_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
- event_trigger_command_tag_check_result result;
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
- result = check_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED)
+ if (commandTag == CMDTAG_UNKNOWN)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
- tag, filtervar)));
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
+ tagstr, filtervar)));
+ if (!command_tag_event_trigger_ok(commandTag))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
-static event_trigger_command_tag_check_result
-check_ddl_tag(const char *tag)
-{
- const char *obtypename;
- const event_trigger_support_data *etsd;
-
- /*
- * Handle some idiosyncratic special cases.
- */
- if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
- pg_strcasecmp(tag, "SELECT INTO") == 0 ||
- pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
- pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
- pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
- pg_strcasecmp(tag, "COMMENT") == 0 ||
- pg_strcasecmp(tag, "GRANT") == 0 ||
- pg_strcasecmp(tag, "REVOKE") == 0 ||
- pg_strcasecmp(tag, "DROP OWNED") == 0 ||
- pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
- pg_strcasecmp(tag, "SECURITY LABEL") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
- /*
- * Otherwise, command should be CREATE, ALTER, or DROP.
- */
- if (pg_strncasecmp(tag, "CREATE ", 7) == 0)
- obtypename = tag + 7;
- else if (pg_strncasecmp(tag, "ALTER ", 6) == 0)
- obtypename = tag + 6;
- else if (pg_strncasecmp(tag, "DROP ", 5) == 0)
- obtypename = tag + 5;
- else
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
-
- /*
- * ...and the object type should be something recognizable.
- */
- for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++)
- if (pg_strcasecmp(etsd->obtypename, obtypename) == 0)
- break;
- if (etsd->obtypename == NULL)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
- if (!etsd->supported)
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-}
-
/*
* Validate DDL command tags for event table_rewrite.
*/
@@ -334,29 +231,18 @@ validate_table_rewrite_tags(const char *filtervar, List *taglist)
foreach(lc, taglist)
{
- const char *tag = strVal(lfirst(lc));
- event_trigger_command_tag_check_result result;
+ const char *tagstr = strVal(lfirst(lc));
+ CommandTag commandTag = GetCommandTagEnum(tagstr);
- result = check_table_rewrite_ddl_tag(tag);
- if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
+ if (!command_tag_table_rewrite_ok(commandTag))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s represents an SQL statement name */
errmsg("event triggers are not supported for %s",
- tag)));
+ tagstr)));
}
}
-static event_trigger_command_tag_check_result
-check_table_rewrite_ddl_tag(const char *tag)
-{
- if (pg_strcasecmp(tag, "ALTER TABLE") == 0 ||
- pg_strcasecmp(tag, "ALTER TYPE") == 0)
- return EVENT_TRIGGER_COMMAND_TAG_OK;
-
- return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
-}
-
/*
* Complain about a duplicate filter variable.
*/
@@ -663,7 +549,7 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
* tags matching.
*/
static bool
-filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
+filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
{
/*
* Filter by session replication role, knowing that we never see disabled
@@ -681,9 +567,7 @@ filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
}
/* Filter by tags, if any were specified. */
- if (item->ntags != 0 && bsearch(tag, item->tag,
- item->ntags, sizeof(char *),
- pg_qsort_strcmp) == NULL)
+ if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
return false;
/* if we reach that point, we're not filtering out this item */
@@ -700,7 +584,7 @@ EventTriggerCommonSetup(Node *parsetree,
EventTriggerEvent event, const char *eventstr,
EventTriggerData *trigdata)
{
- const char *tag;
+ CommandTag tag;
List *cachelist;
ListCell *lc;
List *runlist = NIL;
@@ -716,25 +600,25 @@ EventTriggerCommonSetup(Node *parsetree,
*
* If this cross-check fails for you, you probably need to either adjust
* standard_ProcessUtility() not to invoke event triggers for the command
- * type in question, or you need to adjust check_ddl_tag to accept the
+ * type in question, or you need to adjust event_trigger_ok to accept the
* relevant command tag.
*/
#ifdef USE_ASSERT_CHECKING
{
- const char *dbgtag;
+ CommandTag dbgtag;
dbgtag = CreateCommandTag(parsetree);
if (event == EVT_DDLCommandStart ||
event == EVT_DDLCommandEnd ||
event == EVT_SQLDrop)
{
- if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ if (!command_tag_event_trigger_ok(dbgtag))
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
else if (event == EVT_TableRewrite)
{
- if (check_table_rewrite_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
- elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ if (!command_tag_table_rewrite_ok(dbgtag))
+ elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
}
}
#endif
@@ -758,7 +642,7 @@ EventTriggerCommonSetup(Node *parsetree,
{
EventTriggerCacheItem *item = lfirst(lc);
- if (filter_event_trigger(&tag, item))
+ if (filter_event_trigger(tag, item))
{
/* We must plan to fire this trigger. */
runlist = lappend_oid(runlist, item->fnoid);
@@ -2136,7 +2020,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
values[i++] = Int32GetDatum(addr.objectSubId);
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
/* object_type */
values[i++] = CStringGetTextDatum(type);
/* schema */
@@ -2161,7 +2045,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
/* objsubid */
nulls[i++] = true;
/* command tag */
- values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+ values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
/* object_type */
values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
/* schema */
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 1ee37c1aeb..c3954f3e24 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -136,7 +136,7 @@ SetMatViewPopulatedState(Relation relation, bool newstate)
*/
ObjectAddress
ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag)
+ ParamListInfo params, QueryCompletion *qc)
{
Oid matviewOid;
Relation matviewRel;
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 7e5c805a1e..40be5069fe 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -106,7 +106,7 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
PortalDefineQuery(portal,
NULL,
queryString,
- "SELECT", /* cursor's query is always a SELECT */
+ CMDTAG_SELECT, /* cursor's query is always a SELECT */
list_make1(plan),
NULL);
@@ -160,15 +160,14 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
*
* stmt: parsetree node for command
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store a command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*/
void
PerformPortalFetch(FetchStmt *stmt,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Portal portal;
uint64 nprocessed;
@@ -203,10 +202,9 @@ PerformPortalFetch(FetchStmt *stmt,
dest);
/* Return command status if wanted */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT,
- stmt->ismove ? "MOVE" : "FETCH",
- nprocessed);
+ if (qc)
+ SetQueryCompletion(qc, stmt->ismove ? CMDTAG_MOVE : CMDTAG_FETCH,
+ nprocessed);
}
/*
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index c4e4b6eaec..f917fc9c7a 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -187,7 +187,7 @@ void
ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
PreparedStatement *entry;
CachedPlan *cplan;
@@ -288,7 +288,7 @@ ExecuteQuery(ParseState *pstate,
*/
PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
- (void) PortalRun(portal, count, false, true, dest, dest, completionTag);
+ (void) PortalRun(portal, count, false, true, dest, dest, qc);
PortalDrop(portal, false);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index ee5c3a60ff..28130fbc2b 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -787,11 +787,11 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
if (isTempNamespace(get_rel_namespace(rte->relid)))
continue;
- PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
+ PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt));
}
if (plannedstmt->commandType != CMD_SELECT || plannedstmt->hasModifyingCTE)
- PreventCommandIfParallelMode(CreateCommandTag((Node *) plannedstmt));
+ PreventCommandIfParallelMode(CreateCommandName((Node *) plannedstmt));
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 5cff6c4321..9b45a8a9a0 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -530,7 +530,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a SQL function",
- CreateCommandTag(stmt->utilityStmt))));
+ CreateCommandName(stmt->utilityStmt))));
}
if (fcache->readonly_func && !CommandIsReadOnly(stmt))
@@ -538,7 +538,7 @@ init_execution_state(List *queryTree_list,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ CreateCommandName((Node *) stmt))));
/* OK, build the execution_state for this query */
newes = (execution_state *) palloc(sizeof(execution_state));
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index c46764bf42..dfcf2dba80 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1338,7 +1338,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
/* translator: %s is name of a SQL command, eg INSERT */
errmsg("cannot open %s query as cursor",
- plansource->commandTag)));
+ GetCommandTagName(plansource->commandTag))));
}
Assert(list_length(plan->plancache_list) == 1);
@@ -1469,7 +1469,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) pstmt))));
+ CreateCommandName((Node *) pstmt))));
}
}
@@ -2255,7 +2255,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag((Node *) stmt))));
+ CreateCommandName((Node *) stmt))));
/*
* If not read-only mode, advance the command counter before each
@@ -2291,9 +2291,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else
{
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
ProcessUtilityContext context;
+ InitializeQueryCompletion(&qc);
+
/*
* If the SPI context is atomic, or we are asked to manage
* snapshots, then we are in an atomic execution context.
@@ -2312,7 +2314,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
paramLI,
_SPI_current->queryEnv,
dest,
- completionTag);
+ &qc);
/* Update "processed" if stmt returned tuples */
if (_SPI_current->tuptable)
@@ -2328,9 +2330,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
{
CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
- if (strncmp(completionTag, "SELECT ", 7) == 0)
- _SPI_current->processed =
- pg_strtouint64(completionTag + 7, NULL, 10);
+ if (qc.commandTag == CMDTAG_SELECT)
+ _SPI_current->processed = qc.nprocessed;
else
{
/*
@@ -2351,9 +2352,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else if (IsA(stmt->utilityStmt, CopyStmt))
{
- Assert(strncmp(completionTag, "COPY ", 5) == 0);
- _SPI_current->processed = pg_strtouint64(completionTag + 5,
- NULL, 10);
+ Assert(qc.commandTag == CMDTAG_COPY);
+ _SPI_current->processed = qc.nprocessed;
}
}
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 0ddc707def..c2e5e3abf8 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -100,7 +100,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
buf.record = record;
/* cast so we get a warning when new rmgrs are added */
- switch ((RmgrIds) XLogRecGetRmid(record))
+ switch ((RmgrId) XLogRecGetRmid(record))
{
/*
* Rmgrs we care about for logical decoding. Add new rmgrs in
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index abb533b9d0..ae4a9cbe11 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1074,8 +1074,11 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
static void
DropReplicationSlot(DropReplicationSlotCmd *cmd)
{
+ QueryCompletion qc;
+
ReplicationSlotDrop(cmd->slotname, !cmd->wait);
- EndCommand("DROP_REPLICATION_SLOT", DestRemote);
+ SetQueryCompletion(&qc, CMDTAG_DROP_REPLICATION_SLOT, 0);
+ EndCommand(&qc, DestRemote, false);
}
/*
@@ -1086,6 +1089,7 @@ static void
StartLogicalReplication(StartReplicationCmd *cmd)
{
StringInfoData buf;
+ QueryCompletion qc;
/* make sure that our requirements are still fulfilled */
CheckLogicalDecodingRequirements();
@@ -1160,7 +1164,8 @@ StartLogicalReplication(StartReplicationCmd *cmd)
WalSndSetState(WALSNDSTATE_STARTUP);
/* Get out of COPY mode (CommandComplete). */
- EndCommand("COPY 0", DestRemote);
+ SetQueryCompletion(&qc, CMDTAG_COPY, 0);
+ EndCommand(&qc, DestRemote, false);
}
/*
@@ -1464,6 +1469,7 @@ exec_replication_command(const char *cmd_string)
Node *cmd_node;
MemoryContext cmd_context;
MemoryContext old_context;
+ QueryCompletion qc;
/*
* If WAL sender has been told that shutdown is getting close, switch its
@@ -1614,7 +1620,8 @@ exec_replication_command(const char *cmd_string)
MemoryContextDelete(cmd_context);
/* Send CommandComplete message */
- EndCommand("SELECT", DestRemote);
+ SetQueryCompletion(&qc, CMDTAG_SELECT, 0);
+ EndCommand(&qc, DestRemote, true);
/* Report to pgstat that this process is now idle */
pgstat_report_activity(STATE_IDLE, NULL);
@@ -2867,8 +2874,11 @@ WalSndDone(WalSndSendDataCallback send_data)
if (WalSndCaughtUp && sentPtr == replicatedPtr &&
!pq_is_send_pending())
{
+ QueryCompletion qc;
+
/* Inform the standby that XLOG streaming is done */
- EndCommand("COPY 0", DestRemote);
+ SetQueryCompletion(&qc, CMDTAG_COPY, 0);
+ EndCommand(&qc, DestRemote, false);
pq_flush();
proc_exit(0);
diff --git a/src/backend/tcop/Makefile b/src/backend/tcop/Makefile
index c78f1e0a05..f662a7dd1c 100644
--- a/src/backend/tcop/Makefile
+++ b/src/backend/tcop/Makefile
@@ -13,6 +13,7 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = \
+ cmdtag.o \
dest.o \
fastpath.o \
postgres.o \
diff --git a/src/backend/tcop/cmdtag.c b/src/backend/tcop/cmdtag.c
new file mode 100644
index 0000000000..b9fbff612f
--- /dev/null
+++ b/src/backend/tcop/cmdtag.c
@@ -0,0 +1,98 @@
+/*-------------------------------------------------------------------------
+ *
+ * cmdtag.c
+ * Data and routines for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/tcop/cmdtag.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "miscadmin.h"
+#include "tcop/cmdtag.h"
+
+
+typedef struct CommandTagBehavior
+{
+ const char *name;
+ const bool event_trigger_ok;
+ const bool table_rewrite_ok;
+ const bool display_rowcount;
+} CommandTagBehavior;
+
+#define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt) \
+ { name, evtrgok, rwrok, rowcnt },
+
+const CommandTagBehavior tag_behavior[COMMAND_TAG_NEXTTAG] = {
+#include "tcop/cmdtaglist.h"
+};
+
+#undef PG_CMDTAG
+
+void
+InitializeQueryCompletion(QueryCompletion *qc)
+{
+ qc->commandTag = CMDTAG_UNKNOWN;
+ qc->nprocessed = 0;
+}
+
+const char *
+GetCommandTagName(CommandTag commandTag)
+{
+ return tag_behavior[commandTag].name;
+}
+
+bool
+command_tag_display_rowcount(CommandTag commandTag)
+{
+ return tag_behavior[commandTag].display_rowcount;
+}
+
+bool
+command_tag_event_trigger_ok(CommandTag commandTag)
+{
+ return tag_behavior[commandTag].event_trigger_ok;
+}
+
+bool
+command_tag_table_rewrite_ok(CommandTag commandTag)
+{
+ return tag_behavior[commandTag].table_rewrite_ok;
+}
+
+/*
+ * Search CommandTag by name
+ *
+ * Returns CommandTag, or CMDTAG_UNKNOWN if not recognized
+ */
+CommandTag
+GetCommandTagEnum(const char *commandname)
+{
+ const CommandTagBehavior *base,
+ *last,
+ *position;
+ int result;
+
+ if (commandname == NULL || *commandname == '\0')
+ return CMDTAG_UNKNOWN;
+
+ base = tag_behavior;
+ last = tag_behavior + lengthof(tag_behavior) - 1;
+ while (last >= base)
+ {
+ position = base + ((last - base) >> 1);
+ result = pg_strcasecmp(commandname, position->name);
+ if (result == 0)
+ return (CommandTag) (position - tag_behavior);
+ else if (result < 0)
+ last = position - 1;
+ else
+ base = position + 1;
+ }
+ return CMDTAG_UNKNOWN;
+}
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 09c1dcbb53..7208751ec7 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -100,7 +100,7 @@ DestReceiver *None_Receiver = (DestReceiver *) &donothingDR;
* ----------------
*/
void
-BeginCommand(const char *commandTag, CommandDest dest)
+BeginCommand(CommandTag commandTag, CommandDest dest)
{
/* Nothing to do at present */
}
@@ -163,8 +163,12 @@ CreateDestReceiver(CommandDest dest)
* ----------------
*/
void
-EndCommand(const char *commandTag, CommandDest dest)
+EndCommand(const QueryCompletion *qc, CommandDest dest, bool force_undecorated_output)
{
+ char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag tag;
+ const char *tagname;
+
switch (dest)
{
case DestRemote:
@@ -172,11 +176,27 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestRemoteSimple:
/*
- * We assume the commandTag is plain ASCII and therefore requires
- * no encoding conversion.
+ * We assume the tagname is plain ASCII and therefore requires no
+ * encoding conversion.
+ *
+ * We no longer display LastOid, but to preserve the wire
+ * protocol, we write InvalidOid where the LastOid used to be
+ * written.
+ *
+ * All cases where LastOid was written also write nprocessed
+ * count, so just Assert that rather than having an extra test.
*/
- pq_putmessage('C', commandTag, strlen(commandTag) + 1);
- break;
+ tag = qc->commandTag;
+ tagname = GetCommandTagName(tag);
+
+ if (command_tag_display_rowcount(tag) && !force_undecorated_output)
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ tag == CMDTAG_INSERT ?
+ "%s 0 " UINT64_FORMAT : "%s " UINT64_FORMAT,
+ tagname, qc->nprocessed);
+ else
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s", tagname);
+ pq_putmessage('C', completionTag, strlen(completionTag) + 1);
case DestNone:
case DestDebug:
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 23661ae15f..e5ec388296 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1064,8 +1064,8 @@ exec_simple_query(const char *query_string)
{
RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);
bool snapshot_set = false;
- const char *commandTag;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ CommandTag commandTag;
+ QueryCompletion qc;
MemoryContext per_parsetree_context = NULL;
List *querytree_list,
*plantree_list;
@@ -1081,7 +1081,7 @@ exec_simple_query(const char *query_string)
*/
commandTag = CreateCommandTag(parsetree->stmt);
- set_ps_display(commandTag, false);
+ set_ps_display(GetCommandTagName(commandTag), false);
BeginCommand(commandTag, dest);
@@ -1239,7 +1239,7 @@ exec_simple_query(const char *query_string)
true,
receiver,
receiver,
- completionTag);
+ &qc);
receiver->rDestroy(receiver);
@@ -1290,7 +1290,7 @@ exec_simple_query(const char *query_string)
* command the client sent, regardless of rewriting. (But a command
* aborted by error will not send an EndCommand report at all.)
*/
- EndCommand(completionTag, dest);
+ EndCommand(&qc, dest, false);
/* Now we may drop the per-parsetree context, if one was created. */
if (per_parsetree_context)
@@ -1352,7 +1352,7 @@ exec_parse_message(const char *query_string, /* string to execute */
MemoryContext oldcontext;
List *parsetree_list;
RawStmt *raw_parse_tree;
- const char *commandTag;
+ CommandTag commandTag;
List *querytree_list;
CachedPlanSource *psrc;
bool is_named;
@@ -1514,7 +1514,7 @@ exec_parse_message(const char *query_string, /* string to execute */
{
/* Empty input string. This is legal. */
raw_parse_tree = NULL;
- commandTag = NULL;
+ commandTag = CMDTAG_UNKNOWN;
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
querytree_list = NIL;
}
@@ -2031,7 +2031,7 @@ exec_execute_message(const char *portal_name, long max_rows)
DestReceiver *receiver;
Portal portal;
bool completed;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
const char *sourceText;
const char *prepStmtName;
ParamListInfo portalParams;
@@ -2058,7 +2058,7 @@ exec_execute_message(const char *portal_name, long max_rows)
* If the original query was a null string, just return
* EmptyQueryResponse.
*/
- if (portal->commandTag == NULL)
+ if (portal->commandTag == CMDTAG_UNKNOWN)
{
Assert(portal->stmts == NIL);
NullCommand(dest);
@@ -2104,7 +2104,7 @@ exec_execute_message(const char *portal_name, long max_rows)
pgstat_report_activity(STATE_RUNNING, sourceText);
- set_ps_display(portal->commandTag, false);
+ set_ps_display(GetCommandTagName(portal->commandTag), false);
if (save_log_statement_stats)
ResetUsage();
@@ -2185,7 +2185,7 @@ exec_execute_message(const char *portal_name, long max_rows)
!execute_is_fetch && max_rows == FETCH_ALL,
receiver,
receiver,
- completionTag);
+ &qc);
receiver->rDestroy(receiver);
@@ -2218,7 +2218,7 @@ exec_execute_message(const char *portal_name, long max_rows)
}
/* Send appropriate CommandComplete to client */
- EndCommand(completionTag, dest);
+ EndCommand(&qc, dest, false);
}
else
{
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 0f5801e046..5781fb2e55 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -40,7 +40,7 @@ static void ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletion *qc);
static void FillPortalStore(Portal portal, bool isTopLevel);
static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
DestReceiver *dest);
@@ -48,11 +48,11 @@ static uint64 PortalRunSelect(Portal portal, bool forward, long count,
DestReceiver *dest);
static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
static void PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletion *qc);
static uint64 DoPortalRunFetch(Portal portal,
FetchDirection fdirection,
long count,
@@ -125,10 +125,9 @@ FreeQueryDesc(QueryDesc *qdesc)
* sourceText: the source text of the query
* params: any parameters needed
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store the command completion status data.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want a status string.
*
* Must be called in a memory context that will be reset or deleted on
* error; otherwise the executor's memory usage will be leaked.
@@ -139,7 +138,7 @@ ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
QueryDesc *queryDesc;
@@ -161,38 +160,26 @@ ProcessQuery(PlannedStmt *plan,
ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
/*
- * Build command completion status string, if caller wants one.
+ * Build command completion status data, if caller wants one.
*/
- if (completionTag)
+ if (qc)
{
- Oid lastOid;
-
switch (queryDesc->operation)
{
case CMD_SELECT:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, CMDTAG_SELECT, queryDesc->estate->es_processed);
break;
case CMD_INSERT:
- /* lastoid doesn't exist anymore */
- lastOid = InvalidOid;
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "INSERT %u " UINT64_FORMAT,
- lastOid, queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, CMDTAG_INSERT, queryDesc->estate->es_processed);
break;
case CMD_UPDATE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "UPDATE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, CMDTAG_UPDATE, queryDesc->estate->es_processed);
break;
case CMD_DELETE:
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "DELETE " UINT64_FORMAT,
- queryDesc->estate->es_processed);
+ SetQueryCompletion(qc, CMDTAG_DELETE, queryDesc->estate->es_processed);
break;
default:
- strcpy(completionTag, "???");
+ SetQueryCompletion(qc, CMDTAG_UNKNOWN, queryDesc->estate->es_processed);
break;
}
}
@@ -675,9 +662,8 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
*
* altdest: where to send output of non-primary queries
*
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
- * May be NULL if caller doesn't want a status string.
+ * qc: where to store command completion status data.
+ * May be NULL if caller doesn't want status data.
*
* Returns true if the portal's execution is complete, false if it was
* suspended due to exhaustion of the count parameter.
@@ -685,7 +671,7 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
bool
PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletion *qc)
{
bool result;
uint64 nprocessed;
@@ -700,9 +686,9 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
TRACE_POSTGRESQL_QUERY_EXECUTE_START();
- /* Initialize completion tag to empty string */
- if (completionTag)
- completionTag[0] = '\0';
+ /* Initialize empty completion data */
+ if (qc)
+ InitializeQueryCompletion(qc);
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
{
@@ -771,16 +757,13 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
/*
* If the portal result contains a command tag and the caller
- * gave us a pointer to store it, copy it. Patch the "SELECT"
- * tag to also provide the rowcount.
+ * gave us a pointer to store it, copy it and update the
+ * rowcount.
*/
- if (completionTag && portal->commandTag)
+ if (qc && portal->qc.commandTag != CMDTAG_UNKNOWN)
{
- if (strcmp(portal->commandTag, "SELECT") == 0)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "SELECT " UINT64_FORMAT, nprocessed);
- else
- strcpy(completionTag, portal->commandTag);
+ CopyQueryCompletion(qc, &portal->qc);
+ qc->nprocessed = nprocessed;
}
/* Mark portal not active */
@@ -794,7 +777,7 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
case PORTAL_MULTI_QUERY:
PortalRunMulti(portal, isTopLevel, false,
- dest, altdest, completionTag);
+ dest, altdest, qc);
/* Prevent portal's commands from being re-executed */
MarkPortalDone(portal);
@@ -1005,8 +988,9 @@ static void
FillPortalStore(Portal portal, bool isTopLevel)
{
DestReceiver *treceiver;
- char completionTag[COMPLETION_TAG_BUFSIZE];
+ QueryCompletion qc;
+ InitializeQueryCompletion(&qc);
PortalCreateHoldStore(portal);
treceiver = CreateDestReceiver(DestTuplestore);
SetTuplestoreDestReceiverParams(treceiver,
@@ -1014,8 +998,6 @@ FillPortalStore(Portal portal, bool isTopLevel)
portal->holdContext,
false);
- completionTag[0] = '\0';
-
switch (portal->strategy)
{
case PORTAL_ONE_RETURNING:
@@ -1028,12 +1010,12 @@ FillPortalStore(Portal portal, bool isTopLevel)
* portal's holdSnapshot to the snapshot used (or a copy of it).
*/
PortalRunMulti(portal, isTopLevel, true,
- treceiver, None_Receiver, completionTag);
+ treceiver, None_Receiver, &qc);
break;
case PORTAL_UTIL_SELECT:
PortalRunUtility(portal, linitial_node(PlannedStmt, portal->stmts),
- isTopLevel, true, treceiver, completionTag);
+ isTopLevel, true, treceiver, &qc);
break;
default:
@@ -1042,9 +1024,9 @@ FillPortalStore(Portal portal, bool isTopLevel)
break;
}
- /* Override default completion tag with actual command result */
- if (completionTag[0] != '\0')
- portal->commandTag = pstrdup(completionTag);
+ /* Override portal completion data with actual command results */
+ if (qc.commandTag != CMDTAG_UNKNOWN)
+ CopyQueryCompletion(&portal->qc, &qc);
treceiver->rDestroy(treceiver);
}
@@ -1130,7 +1112,7 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count,
static void
PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
Node *utilityStmt = pstmt->utilityStmt;
Snapshot snapshot;
@@ -1178,7 +1160,7 @@ PortalRunUtility(Portal portal, PlannedStmt *pstmt,
portal->portalParams,
portal->queryEnv,
dest,
- completionTag);
+ qc);
/* Some utility statements may change context on us */
MemoryContextSwitchTo(portal->portalContext);
@@ -1202,7 +1184,7 @@ static void
PortalRunMulti(Portal portal,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, DestReceiver *altdest,
- char *completionTag)
+ QueryCompletion *qc)
{
bool active_snapshot_set = false;
ListCell *stmtlist_item;
@@ -1284,7 +1266,7 @@ PortalRunMulti(Portal portal,
portal->sourceText,
portal->portalParams,
portal->queryEnv,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1319,7 +1301,7 @@ PortalRunMulti(Portal portal,
Assert(!active_snapshot_set);
/* statement can set tag string */
PortalRunUtility(portal, pstmt, isTopLevel, false,
- dest, completionTag);
+ dest, qc);
}
else
{
@@ -1350,8 +1332,8 @@ PortalRunMulti(Portal portal,
PopActiveSnapshot();
/*
- * If a command completion tag was supplied, use it. Otherwise use the
- * portal's commandTag as the default completion tag.
+ * If a query completion data was supplied, use it. Otherwise use the
+ * portal's query completion data.
*
* Exception: Clients expect INSERT/UPDATE/DELETE tags to have counts, so
* fake them with zeros. This can happen with DO INSTEAD rules if there
@@ -1361,18 +1343,12 @@ PortalRunMulti(Portal portal,
* e.g. an INSERT that does an UPDATE instead should not print "0 1" if
* one row was updated. See QueryRewrite(), step 3, for details.
*/
- if (completionTag && completionTag[0] == '\0')
+ if (qc && qc->commandTag == CMDTAG_UNKNOWN)
{
- if (portal->commandTag)
- strcpy(completionTag, portal->commandTag);
- if (strcmp(completionTag, "SELECT") == 0)
- sprintf(completionTag, "SELECT 0 0");
- else if (strcmp(completionTag, "INSERT") == 0)
- strcpy(completionTag, "INSERT 0 0");
- else if (strcmp(completionTag, "UPDATE") == 0)
- strcpy(completionTag, "UPDATE 0");
- else if (strcmp(completionTag, "DELETE") == 0)
- strcpy(completionTag, "DELETE 0");
+ if (portal->qc.commandTag != CMDTAG_UNKNOWN)
+ CopyQueryCompletion(qc, &portal->qc);
+ /* If the caller supplied a qc, we should have set it by now. */
+ Assert(qc->commandTag != CMDTAG_UNKNOWN);
}
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index bb85b5e52a..e9f4820b77 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -75,7 +75,7 @@
ProcessUtility_hook_type ProcessUtility_hook = NULL;
/* local function declarations */
-static int ClassifyUtilityCommandAsReadOnly(Node *parsetree);
+static int ClassifyUtilityCommandAsReadOnly(Node *parsetree);
static void ProcessUtilitySlow(ParseState *pstate,
PlannedStmt *pstmt,
const char *queryString,
@@ -83,10 +83,9 @@ static void ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag);
+ QueryCompletion *qc);
static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
-
/*
* CommandIsReadOnly: is an executable query read-only?
*
@@ -467,7 +466,6 @@ CheckRestrictedOperation(const char *cmdname)
cmdname)));
}
-
/*
* ProcessUtility
* general utility function invoker
@@ -480,16 +478,13 @@ CheckRestrictedOperation(const char *cmdname)
* queryEnv: environment for parse through execution (e.g., ephemeral named
* tables like trigger transition tables). May be NULL.
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
+ * qc: where to store command completion status data.
*
* Caller MUST supply a queryString; it is not allowed (anymore) to pass NULL.
* If you really don't have source text, you can pass a constant string,
* perhaps "(query not available)".
*
- * completionTag is only set nonempty if we want to return a nondefault status.
- *
- * completionTag may be NULL if caller doesn't want a status string.
+ * qc may be NULL if caller doesn't want status data.
*
* Note for users of ProcessUtility_hook: the same queryString may be passed
* to multiple invocations of ProcessUtility when processing a query string
@@ -507,7 +502,7 @@ ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Assert(IsA(pstmt, PlannedStmt));
Assert(pstmt->commandType == CMD_UTILITY);
@@ -521,11 +516,11 @@ ProcessUtility(PlannedStmt *pstmt,
if (ProcessUtility_hook)
(*ProcessUtility_hook) (pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
}
/*
@@ -546,7 +541,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -562,18 +557,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (readonly_flags != COMMAND_IS_STRICTLY_READ_ONLY &&
(XactReadOnly || IsInParallelMode()))
{
- const char *commandtag = CreateCommandTag(parsetree);
+ CommandTag commandtag = CreateCommandTag(parsetree);
if ((readonly_flags & COMMAND_OK_IN_READ_ONLY_TXN) == 0)
- PreventCommandIfReadOnly(commandtag);
+ PreventCommandIfReadOnly(GetCommandTagName(commandtag));
if ((readonly_flags & COMMAND_OK_IN_PARALLEL_MODE) == 0)
- PreventCommandIfParallelMode(commandtag);
+ PreventCommandIfParallelMode(GetCommandTagName(commandtag));
if ((readonly_flags & COMMAND_OK_IN_RECOVERY) == 0)
- PreventCommandDuringRecovery(commandtag);
+ PreventCommandDuringRecovery(GetCommandTagName(commandtag));
}
- if (completionTag)
- completionTag[0] = '\0';
+ if (qc)
+ InitializeQueryCompletion(qc);
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -623,18 +618,18 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case TRANS_STMT_COMMIT:
if (!EndTransactionBlock(stmt->chain))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQueryCompletion(qc, CMDTAG_ROLLBACK, 0);
}
break;
case TRANS_STMT_PREPARE:
if (!PrepareTransactionBlock(stmt->gid))
{
- /* report unsuccessful commit in completionTag */
- if (completionTag)
- strcpy(completionTag, "ROLLBACK");
+ /* report unsuccessful commit in qc */
+ if (qc)
+ SetQueryCompletion(qc, CMDTAG_ROLLBACK, 0);
}
break;
@@ -693,8 +688,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case T_FetchStmt:
- PerformPortalFetch((FetchStmt *) parsetree, dest,
- completionTag);
+ PerformPortalFetch((FetchStmt *) parsetree, dest, qc);
break;
case T_DoStmt:
@@ -729,9 +723,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
DoCopy(pstate, (CopyStmt *) parsetree,
pstmt->stmt_location, pstmt->stmt_len,
&processed);
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "COPY " UINT64_FORMAT, processed);
+ if (qc)
+ SetQueryCompletion(qc, CMDTAG_COPY, processed);
}
break;
@@ -745,7 +738,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
ExecuteQuery(pstate,
(ExecuteStmt *) parsetree, NULL,
params,
- dest, completionTag);
+ dest, qc);
break;
case T_DeallocateStmt:
@@ -974,7 +967,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecuteGrantStmt(stmt);
}
@@ -987,7 +980,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecDropStmt(stmt, isTopLevel);
}
@@ -1000,7 +993,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecRenameStmt(stmt);
}
@@ -1013,7 +1006,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectDependsStmt(stmt, NULL);
}
@@ -1026,7 +1019,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
}
@@ -1039,7 +1032,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecAlterOwnerStmt(stmt);
}
@@ -1052,7 +1045,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
CommentObject(stmt);
break;
@@ -1065,7 +1058,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
ExecSecLabelStmt(stmt);
break;
@@ -1075,7 +1068,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
break;
}
@@ -1102,7 +1095,7 @@ ProcessUtilitySlow(ParseState *pstate,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
- char *completionTag)
+ QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
@@ -1605,7 +1598,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateTableAsStmt:
address = ExecCreateTableAs(pstate, (CreateTableAsStmt *) parsetree,
- params, queryEnv, completionTag);
+ params, queryEnv, qc);
break;
case T_RefreshMatViewStmt:
@@ -1620,7 +1613,7 @@ ProcessUtilitySlow(ParseState *pstate,
PG_TRY();
{
address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
- queryString, params, completionTag);
+ queryString, params, qc);
}
PG_FINALLY();
{
@@ -2099,137 +2092,137 @@ UtilityContainsQuery(Node *parsetree)
*
* This covers most cases where ALTER is used with an ObjectType enum.
*/
-static const char *
+static CommandTag
AlterObjectTypeCommandTag(ObjectType objtype)
{
- const char *tag;
+ CommandTag tag;
switch (objtype)
{
case OBJECT_AGGREGATE:
- tag = "ALTER AGGREGATE";
+ tag = CMDTAG_ALTER_AGGREGATE;
break;
case OBJECT_ATTRIBUTE:
- tag = "ALTER TYPE";
+ tag = CMDTAG_ALTER_TYPE;
break;
case OBJECT_CAST:
- tag = "ALTER CAST";
+ tag = CMDTAG_ALTER_CAST;
break;
case OBJECT_COLLATION:
- tag = "ALTER COLLATION";
+ tag = CMDTAG_ALTER_COLLATION;
break;
case OBJECT_COLUMN:
- tag = "ALTER TABLE";
+ tag = CMDTAG_ALTER_TABLE;
break;
case OBJECT_CONVERSION:
- tag = "ALTER CONVERSION";
+ tag = CMDTAG_ALTER_CONVERSION;
break;
case OBJECT_DATABASE:
- tag = "ALTER DATABASE";
+ tag = CMDTAG_ALTER_DATABASE;
break;
case OBJECT_DOMAIN:
case OBJECT_DOMCONSTRAINT:
- tag = "ALTER DOMAIN";
+ tag = CMDTAG_ALTER_DOMAIN;
break;
case OBJECT_EXTENSION:
- tag = "ALTER EXTENSION";
+ tag = CMDTAG_ALTER_EXTENSION;
break;
case OBJECT_FDW:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = CMDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "ALTER SERVER";
+ tag = CMDTAG_ALTER_SERVER;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "ALTER FOREIGN TABLE";
+ tag = CMDTAG_ALTER_FOREIGN_TABLE;
break;
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = CMDTAG_ALTER_FUNCTION;
break;
case OBJECT_INDEX:
- tag = "ALTER INDEX";
+ tag = CMDTAG_ALTER_INDEX;
break;
case OBJECT_LANGUAGE:
- tag = "ALTER LANGUAGE";
+ tag = CMDTAG_ALTER_LANGUAGE;
break;
case OBJECT_LARGEOBJECT:
- tag = "ALTER LARGE OBJECT";
+ tag = CMDTAG_ALTER_LARGE_OBJECT;
break;
case OBJECT_OPCLASS:
- tag = "ALTER OPERATOR CLASS";
+ tag = CMDTAG_ALTER_OPERATOR_CLASS;
break;
case OBJECT_OPERATOR:
- tag = "ALTER OPERATOR";
+ tag = CMDTAG_ALTER_OPERATOR;
break;
case OBJECT_OPFAMILY:
- tag = "ALTER OPERATOR FAMILY";
+ tag = CMDTAG_ALTER_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "ALTER POLICY";
+ tag = CMDTAG_ALTER_POLICY;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = CMDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROLE:
- tag = "ALTER ROLE";
+ tag = CMDTAG_ALTER_ROLE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = CMDTAG_ALTER_ROUTINE;
break;
case OBJECT_RULE:
- tag = "ALTER RULE";
+ tag = CMDTAG_ALTER_RULE;
break;
case OBJECT_SCHEMA:
- tag = "ALTER SCHEMA";
+ tag = CMDTAG_ALTER_SCHEMA;
break;
case OBJECT_SEQUENCE:
- tag = "ALTER SEQUENCE";
+ tag = CMDTAG_ALTER_SEQUENCE;
break;
case OBJECT_TABLE:
case OBJECT_TABCONSTRAINT:
- tag = "ALTER TABLE";
+ tag = CMDTAG_ALTER_TABLE;
break;
case OBJECT_TABLESPACE:
- tag = "ALTER TABLESPACE";
+ tag = CMDTAG_ALTER_TABLESPACE;
break;
case OBJECT_TRIGGER:
- tag = "ALTER TRIGGER";
+ tag = CMDTAG_ALTER_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "ALTER EVENT TRIGGER";
+ tag = CMDTAG_ALTER_EVENT_TRIGGER;
break;
case OBJECT_TSCONFIGURATION:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_TSDICTIONARY:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSPARSER:
- tag = "ALTER TEXT SEARCH PARSER";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSTEMPLATE:
- tag = "ALTER TEXT SEARCH TEMPLATE";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TYPE:
- tag = "ALTER TYPE";
+ tag = CMDTAG_ALTER_TYPE;
break;
case OBJECT_VIEW:
- tag = "ALTER VIEW";
+ tag = CMDTAG_ALTER_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "ALTER MATERIALIZED VIEW";
+ tag = CMDTAG_ALTER_MATERIALIZED_VIEW;
break;
case OBJECT_PUBLICATION:
- tag = "ALTER PUBLICATION";
+ tag = CMDTAG_ALTER_PUBLICATION;
break;
case OBJECT_SUBSCRIPTION:
- tag = "ALTER SUBSCRIPTION";
+ tag = CMDTAG_ALTER_SUBSCRIPTION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "ALTER STATISTICS";
+ tag = CMDTAG_ALTER_STATISTICS;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
@@ -2238,20 +2231,17 @@ AlterObjectTypeCommandTag(ObjectType objtype)
/*
* CreateCommandTag
- * utility to get a string representation of the command operation,
+ * utility to get a CommandTag for the command operation,
* given either a raw (un-analyzed) parsetree, an analyzed Query,
* or a PlannedStmt.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
- *
- * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
- * Also, the result must point at a true constant (permanent storage).
*/
-const char *
+CommandTag
CreateCommandTag(Node *parsetree)
{
- const char *tag;
+ CommandTag tag;
switch (nodeTag(parsetree))
{
@@ -2262,19 +2252,19 @@ CreateCommandTag(Node *parsetree)
/* raw plannable queries */
case T_InsertStmt:
- tag = "INSERT";
+ tag = CMDTAG_INSERT;
break;
case T_DeleteStmt:
- tag = "DELETE";
+ tag = CMDTAG_DELETE;
break;
case T_UpdateStmt:
- tag = "UPDATE";
+ tag = CMDTAG_UPDATE;
break;
case T_SelectStmt:
- tag = "SELECT";
+ tag = CMDTAG_SELECT;
break;
/* utility statements --- same whether raw or cooked */
@@ -2285,51 +2275,51 @@ CreateCommandTag(Node *parsetree)
switch (stmt->kind)
{
case TRANS_STMT_BEGIN:
- tag = "BEGIN";
+ tag = CMDTAG_BEGIN;
break;
case TRANS_STMT_START:
- tag = "START TRANSACTION";
+ tag = CMDTAG_START_TRANSACTION;
break;
case TRANS_STMT_COMMIT:
- tag = "COMMIT";
+ tag = CMDTAG_COMMIT;
break;
case TRANS_STMT_ROLLBACK:
case TRANS_STMT_ROLLBACK_TO:
- tag = "ROLLBACK";
+ tag = CMDTAG_ROLLBACK;
break;
case TRANS_STMT_SAVEPOINT:
- tag = "SAVEPOINT";
+ tag = CMDTAG_SAVEPOINT;
break;
case TRANS_STMT_RELEASE:
- tag = "RELEASE";
+ tag = CMDTAG_RELEASE;
break;
case TRANS_STMT_PREPARE:
- tag = "PREPARE TRANSACTION";
+ tag = CMDTAG_PREPARE_TRANSACTION;
break;
case TRANS_STMT_COMMIT_PREPARED:
- tag = "COMMIT PREPARED";
+ tag = CMDTAG_COMMIT_PREPARED;
break;
case TRANS_STMT_ROLLBACK_PREPARED:
- tag = "ROLLBACK PREPARED";
+ tag = CMDTAG_ROLLBACK_PREPARED;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
}
break;
case T_DeclareCursorStmt:
- tag = "DECLARE CURSOR";
+ tag = CMDTAG_DECLARE_CURSOR;
break;
case T_ClosePortalStmt:
@@ -2337,9 +2327,9 @@ CreateCommandTag(Node *parsetree)
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
if (stmt->portalname == NULL)
- tag = "CLOSE CURSOR ALL";
+ tag = CMDTAG_CLOSE_CURSOR_ALL;
else
- tag = "CLOSE CURSOR";
+ tag = CMDTAG_CLOSE_CURSOR;
}
break;
@@ -2347,209 +2337,209 @@ CreateCommandTag(Node *parsetree)
{
FetchStmt *stmt = (FetchStmt *) parsetree;
- tag = (stmt->ismove) ? "MOVE" : "FETCH";
+ tag = (stmt->ismove) ? CMDTAG_MOVE : CMDTAG_FETCH;
}
break;
case T_CreateDomainStmt:
- tag = "CREATE DOMAIN";
+ tag = CMDTAG_CREATE_DOMAIN;
break;
case T_CreateSchemaStmt:
- tag = "CREATE SCHEMA";
+ tag = CMDTAG_CREATE_SCHEMA;
break;
case T_CreateStmt:
- tag = "CREATE TABLE";
+ tag = CMDTAG_CREATE_TABLE;
break;
case T_CreateTableSpaceStmt:
- tag = "CREATE TABLESPACE";
+ tag = CMDTAG_CREATE_TABLESPACE;
break;
case T_DropTableSpaceStmt:
- tag = "DROP TABLESPACE";
+ tag = CMDTAG_DROP_TABLESPACE;
break;
case T_AlterTableSpaceOptionsStmt:
- tag = "ALTER TABLESPACE";
+ tag = CMDTAG_ALTER_TABLESPACE;
break;
case T_CreateExtensionStmt:
- tag = "CREATE EXTENSION";
+ tag = CMDTAG_CREATE_EXTENSION;
break;
case T_AlterExtensionStmt:
- tag = "ALTER EXTENSION";
+ tag = CMDTAG_ALTER_EXTENSION;
break;
case T_AlterExtensionContentsStmt:
- tag = "ALTER EXTENSION";
+ tag = CMDTAG_ALTER_EXTENSION;
break;
case T_CreateFdwStmt:
- tag = "CREATE FOREIGN DATA WRAPPER";
+ tag = CMDTAG_CREATE_FOREIGN_DATA_WRAPPER;
break;
case T_AlterFdwStmt:
- tag = "ALTER FOREIGN DATA WRAPPER";
+ tag = CMDTAG_ALTER_FOREIGN_DATA_WRAPPER;
break;
case T_CreateForeignServerStmt:
- tag = "CREATE SERVER";
+ tag = CMDTAG_CREATE_SERVER;
break;
case T_AlterForeignServerStmt:
- tag = "ALTER SERVER";
+ tag = CMDTAG_ALTER_SERVER;
break;
case T_CreateUserMappingStmt:
- tag = "CREATE USER MAPPING";
+ tag = CMDTAG_CREATE_USER_MAPPING;
break;
case T_AlterUserMappingStmt:
- tag = "ALTER USER MAPPING";
+ tag = CMDTAG_ALTER_USER_MAPPING;
break;
case T_DropUserMappingStmt:
- tag = "DROP USER MAPPING";
+ tag = CMDTAG_DROP_USER_MAPPING;
break;
case T_CreateForeignTableStmt:
- tag = "CREATE FOREIGN TABLE";
+ tag = CMDTAG_CREATE_FOREIGN_TABLE;
break;
case T_ImportForeignSchemaStmt:
- tag = "IMPORT FOREIGN SCHEMA";
+ tag = CMDTAG_IMPORT_FOREIGN_SCHEMA;
break;
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_TABLE:
- tag = "DROP TABLE";
+ tag = CMDTAG_DROP_TABLE;
break;
case OBJECT_SEQUENCE:
- tag = "DROP SEQUENCE";
+ tag = CMDTAG_DROP_SEQUENCE;
break;
case OBJECT_VIEW:
- tag = "DROP VIEW";
+ tag = CMDTAG_DROP_VIEW;
break;
case OBJECT_MATVIEW:
- tag = "DROP MATERIALIZED VIEW";
+ tag = CMDTAG_DROP_MATERIALIZED_VIEW;
break;
case OBJECT_INDEX:
- tag = "DROP INDEX";
+ tag = CMDTAG_DROP_INDEX;
break;
case OBJECT_TYPE:
- tag = "DROP TYPE";
+ tag = CMDTAG_DROP_TYPE;
break;
case OBJECT_DOMAIN:
- tag = "DROP DOMAIN";
+ tag = CMDTAG_DROP_DOMAIN;
break;
case OBJECT_COLLATION:
- tag = "DROP COLLATION";
+ tag = CMDTAG_DROP_COLLATION;
break;
case OBJECT_CONVERSION:
- tag = "DROP CONVERSION";
+ tag = CMDTAG_DROP_CONVERSION;
break;
case OBJECT_SCHEMA:
- tag = "DROP SCHEMA";
+ tag = CMDTAG_DROP_SCHEMA;
break;
case OBJECT_TSPARSER:
- tag = "DROP TEXT SEARCH PARSER";
+ tag = CMDTAG_DROP_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "DROP TEXT SEARCH DICTIONARY";
+ tag = CMDTAG_DROP_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "DROP TEXT SEARCH TEMPLATE";
+ tag = CMDTAG_DROP_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "DROP TEXT SEARCH CONFIGURATION";
+ tag = CMDTAG_DROP_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_FOREIGN_TABLE:
- tag = "DROP FOREIGN TABLE";
+ tag = CMDTAG_DROP_FOREIGN_TABLE;
break;
case OBJECT_EXTENSION:
- tag = "DROP EXTENSION";
+ tag = CMDTAG_DROP_EXTENSION;
break;
case OBJECT_FUNCTION:
- tag = "DROP FUNCTION";
+ tag = CMDTAG_DROP_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "DROP PROCEDURE";
+ tag = CMDTAG_DROP_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "DROP ROUTINE";
+ tag = CMDTAG_DROP_ROUTINE;
break;
case OBJECT_AGGREGATE:
- tag = "DROP AGGREGATE";
+ tag = CMDTAG_DROP_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "DROP OPERATOR";
+ tag = CMDTAG_DROP_OPERATOR;
break;
case OBJECT_LANGUAGE:
- tag = "DROP LANGUAGE";
+ tag = CMDTAG_DROP_LANGUAGE;
break;
case OBJECT_CAST:
- tag = "DROP CAST";
+ tag = CMDTAG_DROP_CAST;
break;
case OBJECT_TRIGGER:
- tag = "DROP TRIGGER";
+ tag = CMDTAG_DROP_TRIGGER;
break;
case OBJECT_EVENT_TRIGGER:
- tag = "DROP EVENT TRIGGER";
+ tag = CMDTAG_DROP_EVENT_TRIGGER;
break;
case OBJECT_RULE:
- tag = "DROP RULE";
+ tag = CMDTAG_DROP_RULE;
break;
case OBJECT_FDW:
- tag = "DROP FOREIGN DATA WRAPPER";
+ tag = CMDTAG_DROP_FOREIGN_DATA_WRAPPER;
break;
case OBJECT_FOREIGN_SERVER:
- tag = "DROP SERVER";
+ tag = CMDTAG_DROP_SERVER;
break;
case OBJECT_OPCLASS:
- tag = "DROP OPERATOR CLASS";
+ tag = CMDTAG_DROP_OPERATOR_CLASS;
break;
case OBJECT_OPFAMILY:
- tag = "DROP OPERATOR FAMILY";
+ tag = CMDTAG_DROP_OPERATOR_FAMILY;
break;
case OBJECT_POLICY:
- tag = "DROP POLICY";
+ tag = CMDTAG_DROP_POLICY;
break;
case OBJECT_TRANSFORM:
- tag = "DROP TRANSFORM";
+ tag = CMDTAG_DROP_TRANSFORM;
break;
case OBJECT_ACCESS_METHOD:
- tag = "DROP ACCESS METHOD";
+ tag = CMDTAG_DROP_ACCESS_METHOD;
break;
case OBJECT_PUBLICATION:
- tag = "DROP PUBLICATION";
+ tag = CMDTAG_DROP_PUBLICATION;
break;
case OBJECT_STATISTIC_EXT:
- tag = "DROP STATISTICS";
+ tag = CMDTAG_DROP_STATISTICS;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
case T_TruncateStmt:
- tag = "TRUNCATE TABLE";
+ tag = CMDTAG_TRUNCATE_TABLE;
break;
case T_CommentStmt:
- tag = "COMMENT";
+ tag = CMDTAG_COMMENT;
break;
case T_SecLabelStmt:
- tag = "SECURITY LABEL";
+ tag = CMDTAG_SECURITY_LABEL;
break;
case T_CopyStmt:
- tag = "COPY";
+ tag = CMDTAG_COPY;
break;
case T_RenameStmt:
@@ -2584,23 +2574,23 @@ CreateCommandTag(Node *parsetree)
break;
case T_AlterDomainStmt:
- tag = "ALTER DOMAIN";
+ tag = CMDTAG_ALTER_DOMAIN;
break;
case T_AlterFunctionStmt:
switch (((AlterFunctionStmt *) parsetree)->objtype)
{
case OBJECT_FUNCTION:
- tag = "ALTER FUNCTION";
+ tag = CMDTAG_ALTER_FUNCTION;
break;
case OBJECT_PROCEDURE:
- tag = "ALTER PROCEDURE";
+ tag = CMDTAG_ALTER_PROCEDURE;
break;
case OBJECT_ROUTINE:
- tag = "ALTER ROUTINE";
+ tag = CMDTAG_ALTER_ROUTINE;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
@@ -2608,7 +2598,7 @@ CreateCommandTag(Node *parsetree)
{
GrantStmt *stmt = (GrantStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
+ tag = (stmt->is_grant) ? CMDTAG_GRANT : CMDTAG_REVOKE;
}
break;
@@ -2616,145 +2606,145 @@ CreateCommandTag(Node *parsetree)
{
GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
- tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
+ tag = (stmt->is_grant) ? CMDTAG_GRANT_ROLE : CMDTAG_REVOKE_ROLE;
}
break;
case T_AlterDefaultPrivilegesStmt:
- tag = "ALTER DEFAULT PRIVILEGES";
+ tag = CMDTAG_ALTER_DEFAULT_PRIVILEGES;
break;
case T_DefineStmt:
switch (((DefineStmt *) parsetree)->kind)
{
case OBJECT_AGGREGATE:
- tag = "CREATE AGGREGATE";
+ tag = CMDTAG_CREATE_AGGREGATE;
break;
case OBJECT_OPERATOR:
- tag = "CREATE OPERATOR";
+ tag = CMDTAG_CREATE_OPERATOR;
break;
case OBJECT_TYPE:
- tag = "CREATE TYPE";
+ tag = CMDTAG_CREATE_TYPE;
break;
case OBJECT_TSPARSER:
- tag = "CREATE TEXT SEARCH PARSER";
+ tag = CMDTAG_CREATE_TEXT_SEARCH_PARSER;
break;
case OBJECT_TSDICTIONARY:
- tag = "CREATE TEXT SEARCH DICTIONARY";
+ tag = CMDTAG_CREATE_TEXT_SEARCH_DICTIONARY;
break;
case OBJECT_TSTEMPLATE:
- tag = "CREATE TEXT SEARCH TEMPLATE";
+ tag = CMDTAG_CREATE_TEXT_SEARCH_TEMPLATE;
break;
case OBJECT_TSCONFIGURATION:
- tag = "CREATE TEXT SEARCH CONFIGURATION";
+ tag = CMDTAG_CREATE_TEXT_SEARCH_CONFIGURATION;
break;
case OBJECT_COLLATION:
- tag = "CREATE COLLATION";
+ tag = CMDTAG_CREATE_COLLATION;
break;
case OBJECT_ACCESS_METHOD:
- tag = "CREATE ACCESS METHOD";
+ tag = CMDTAG_CREATE_ACCESS_METHOD;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
case T_CompositeTypeStmt:
- tag = "CREATE TYPE";
+ tag = CMDTAG_CREATE_TYPE;
break;
case T_CreateEnumStmt:
- tag = "CREATE TYPE";
+ tag = CMDTAG_CREATE_TYPE;
break;
case T_CreateRangeStmt:
- tag = "CREATE TYPE";
+ tag = CMDTAG_CREATE_TYPE;
break;
case T_AlterEnumStmt:
- tag = "ALTER TYPE";
+ tag = CMDTAG_ALTER_TYPE;
break;
case T_ViewStmt:
- tag = "CREATE VIEW";
+ tag = CMDTAG_CREATE_VIEW;
break;
case T_CreateFunctionStmt:
if (((CreateFunctionStmt *) parsetree)->is_procedure)
- tag = "CREATE PROCEDURE";
+ tag = CMDTAG_CREATE_PROCEDURE;
else
- tag = "CREATE FUNCTION";
+ tag = CMDTAG_CREATE_FUNCTION;
break;
case T_IndexStmt:
- tag = "CREATE INDEX";
+ tag = CMDTAG_CREATE_INDEX;
break;
case T_RuleStmt:
- tag = "CREATE RULE";
+ tag = CMDTAG_CREATE_RULE;
break;
case T_CreateSeqStmt:
- tag = "CREATE SEQUENCE";
+ tag = CMDTAG_CREATE_SEQUENCE;
break;
case T_AlterSeqStmt:
- tag = "ALTER SEQUENCE";
+ tag = CMDTAG_ALTER_SEQUENCE;
break;
case T_DoStmt:
- tag = "DO";
+ tag = CMDTAG_DO;
break;
case T_CreatedbStmt:
- tag = "CREATE DATABASE";
+ tag = CMDTAG_CREATE_DATABASE;
break;
case T_AlterDatabaseStmt:
- tag = "ALTER DATABASE";
+ tag = CMDTAG_ALTER_DATABASE;
break;
case T_AlterDatabaseSetStmt:
- tag = "ALTER DATABASE";
+ tag = CMDTAG_ALTER_DATABASE;
break;
case T_DropdbStmt:
- tag = "DROP DATABASE";
+ tag = CMDTAG_DROP_DATABASE;
break;
case T_NotifyStmt:
- tag = "NOTIFY";
+ tag = CMDTAG_NOTIFY;
break;
case T_ListenStmt:
- tag = "LISTEN";
+ tag = CMDTAG_LISTEN;
break;
case T_UnlistenStmt:
- tag = "UNLISTEN";
+ tag = CMDTAG_UNLISTEN;
break;
case T_LoadStmt:
- tag = "LOAD";
+ tag = CMDTAG_LOAD;
break;
case T_CallStmt:
- tag = "CALL";
+ tag = CMDTAG_CALL;
break;
case T_ClusterStmt:
- tag = "CLUSTER";
+ tag = CMDTAG_CLUSTER;
break;
case T_VacuumStmt:
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
- tag = "VACUUM";
+ tag = CMDTAG_VACUUM;
else
- tag = "ANALYZE";
+ tag = CMDTAG_ANALYZE;
break;
case T_ExplainStmt:
- tag = "EXPLAIN";
+ tag = CMDTAG_EXPLAIN;
break;
case T_CreateTableAsStmt:
@@ -2762,24 +2752,24 @@ CreateCommandTag(Node *parsetree)
{
case OBJECT_TABLE:
if (((CreateTableAsStmt *) parsetree)->is_select_into)
- tag = "SELECT INTO";
+ tag = CMDTAG_SELECT_INTO;
else
- tag = "CREATE TABLE AS";
+ tag = CMDTAG_CREATE_TABLE_AS;
break;
case OBJECT_MATVIEW:
- tag = "CREATE MATERIALIZED VIEW";
+ tag = CMDTAG_CREATE_MATERIALIZED_VIEW;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
case T_RefreshMatViewStmt:
- tag = "REFRESH MATERIALIZED VIEW";
+ tag = CMDTAG_REFRESH_MATERIALIZED_VIEW;
break;
case T_AlterSystemStmt:
- tag = "ALTER SYSTEM";
+ tag = CMDTAG_ALTER_SYSTEM;
break;
case T_VariableSetStmt:
@@ -2789,183 +2779,183 @@ CreateCommandTag(Node *parsetree)
case VAR_SET_CURRENT:
case VAR_SET_DEFAULT:
case VAR_SET_MULTI:
- tag = "SET";
+ tag = CMDTAG_SET;
break;
case VAR_RESET:
case VAR_RESET_ALL:
- tag = "RESET";
+ tag = CMDTAG_RESET;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
case T_VariableShowStmt:
- tag = "SHOW";
+ tag = CMDTAG_SHOW;
break;
case T_DiscardStmt:
switch (((DiscardStmt *) parsetree)->target)
{
case DISCARD_ALL:
- tag = "DISCARD ALL";
+ tag = CMDTAG_DISCARD_ALL;
break;
case DISCARD_PLANS:
- tag = "DISCARD PLANS";
+ tag = CMDTAG_DISCARD_PLANS;
break;
case DISCARD_TEMP:
- tag = "DISCARD TEMP";
+ tag = CMDTAG_DISCARD_TEMP;
break;
case DISCARD_SEQUENCES:
- tag = "DISCARD SEQUENCES";
+ tag = CMDTAG_DISCARD_SEQUENCES;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
}
break;
case T_CreateTransformStmt:
- tag = "CREATE TRANSFORM";
+ tag = CMDTAG_CREATE_TRANSFORM;
break;
case T_CreateTrigStmt:
- tag = "CREATE TRIGGER";
+ tag = CMDTAG_CREATE_TRIGGER;
break;
case T_CreateEventTrigStmt:
- tag = "CREATE EVENT TRIGGER";
+ tag = CMDTAG_CREATE_EVENT_TRIGGER;
break;
case T_AlterEventTrigStmt:
- tag = "ALTER EVENT TRIGGER";
+ tag = CMDTAG_ALTER_EVENT_TRIGGER;
break;
case T_CreatePLangStmt:
- tag = "CREATE LANGUAGE";
+ tag = CMDTAG_CREATE_LANGUAGE;
break;
case T_CreateRoleStmt:
- tag = "CREATE ROLE";
+ tag = CMDTAG_CREATE_ROLE;
break;
case T_AlterRoleStmt:
- tag = "ALTER ROLE";
+ tag = CMDTAG_ALTER_ROLE;
break;
case T_AlterRoleSetStmt:
- tag = "ALTER ROLE";
+ tag = CMDTAG_ALTER_ROLE;
break;
case T_DropRoleStmt:
- tag = "DROP ROLE";
+ tag = CMDTAG_DROP_ROLE;
break;
case T_DropOwnedStmt:
- tag = "DROP OWNED";
+ tag = CMDTAG_DROP_OWNED;
break;
case T_ReassignOwnedStmt:
- tag = "REASSIGN OWNED";
+ tag = CMDTAG_REASSIGN_OWNED;
break;
case T_LockStmt:
- tag = "LOCK TABLE";
+ tag = CMDTAG_LOCK_TABLE;
break;
case T_ConstraintsSetStmt:
- tag = "SET CONSTRAINTS";
+ tag = CMDTAG_SET_CONSTRAINTS;
break;
case T_CheckPointStmt:
- tag = "CHECKPOINT";
+ tag = CMDTAG_CHECKPOINT;
break;
case T_ReindexStmt:
- tag = "REINDEX";
+ tag = CMDTAG_REINDEX;
break;
case T_CreateConversionStmt:
- tag = "CREATE CONVERSION";
+ tag = CMDTAG_CREATE_CONVERSION;
break;
case T_CreateCastStmt:
- tag = "CREATE CAST";
+ tag = CMDTAG_CREATE_CAST;
break;
case T_CreateOpClassStmt:
- tag = "CREATE OPERATOR CLASS";
+ tag = CMDTAG_CREATE_OPERATOR_CLASS;
break;
case T_CreateOpFamilyStmt:
- tag = "CREATE OPERATOR FAMILY";
+ tag = CMDTAG_CREATE_OPERATOR_FAMILY;
break;
case T_AlterOpFamilyStmt:
- tag = "ALTER OPERATOR FAMILY";
+ tag = CMDTAG_ALTER_OPERATOR_FAMILY;
break;
case T_AlterOperatorStmt:
- tag = "ALTER OPERATOR";
+ tag = CMDTAG_ALTER_OPERATOR;
break;
case T_AlterTSDictionaryStmt:
- tag = "ALTER TEXT SEARCH DICTIONARY";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_DICTIONARY;
break;
case T_AlterTSConfigurationStmt:
- tag = "ALTER TEXT SEARCH CONFIGURATION";
+ tag = CMDTAG_ALTER_TEXT_SEARCH_CONFIGURATION;
break;
case T_CreatePolicyStmt:
- tag = "CREATE POLICY";
+ tag = CMDTAG_CREATE_POLICY;
break;
case T_AlterPolicyStmt:
- tag = "ALTER POLICY";
+ tag = CMDTAG_ALTER_POLICY;
break;
case T_CreateAmStmt:
- tag = "CREATE ACCESS METHOD";
+ tag = CMDTAG_CREATE_ACCESS_METHOD;
break;
case T_CreatePublicationStmt:
- tag = "CREATE PUBLICATION";
+ tag = CMDTAG_CREATE_PUBLICATION;
break;
case T_AlterPublicationStmt:
- tag = "ALTER PUBLICATION";
+ tag = CMDTAG_ALTER_PUBLICATION;
break;
case T_CreateSubscriptionStmt:
- tag = "CREATE SUBSCRIPTION";
+ tag = CMDTAG_CREATE_SUBSCRIPTION;
break;
case T_AlterSubscriptionStmt:
- tag = "ALTER SUBSCRIPTION";
+ tag = CMDTAG_ALTER_SUBSCRIPTION;
break;
case T_DropSubscriptionStmt:
- tag = "DROP SUBSCRIPTION";
+ tag = CMDTAG_DROP_SUBSCRIPTION;
break;
case T_AlterCollationStmt:
- tag = "ALTER COLLATION";
+ tag = CMDTAG_ALTER_COLLATION;
break;
case T_PrepareStmt:
- tag = "PREPARE";
+ tag = CMDTAG_PREPARE;
break;
case T_ExecuteStmt:
- tag = "EXECUTE";
+ tag = CMDTAG_EXECUTE;
break;
case T_CreateStatsStmt:
- tag = "CREATE STATISTICS";
+ tag = CMDTAG_CREATE_STATISTICS;
break;
case T_AlterStatsStmt:
- tag = "ALTER STATISTICS";
+ tag = CMDTAG_ALTER_STATISTICS;
break;
case T_DeallocateStmt:
@@ -2973,9 +2963,9 @@ CreateCommandTag(Node *parsetree)
DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
if (stmt->name == NULL)
- tag = "DEALLOCATE ALL";
+ tag = CMDTAG_DEALLOCATE_ALL;
else
- tag = "DEALLOCATE";
+ tag = CMDTAG_DEALLOCATE;
}
break;
@@ -2999,33 +2989,33 @@ CreateCommandTag(Node *parsetree)
switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = CMDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = CMDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = CMDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = CMDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "SELECT";
+ tag = CMDTAG_SELECT;
break;
}
}
else
- tag = "SELECT";
+ tag = CMDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = CMDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = CMDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = CMDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3033,7 +3023,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
}
@@ -3059,33 +3049,33 @@ CreateCommandTag(Node *parsetree)
switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
{
case LCS_FORKEYSHARE:
- tag = "SELECT FOR KEY SHARE";
+ tag = CMDTAG_SELECT_FOR_KEY_SHARE;
break;
case LCS_FORSHARE:
- tag = "SELECT FOR SHARE";
+ tag = CMDTAG_SELECT_FOR_SHARE;
break;
case LCS_FORNOKEYUPDATE:
- tag = "SELECT FOR NO KEY UPDATE";
+ tag = CMDTAG_SELECT_FOR_NO_KEY_UPDATE;
break;
case LCS_FORUPDATE:
- tag = "SELECT FOR UPDATE";
+ tag = CMDTAG_SELECT_FOR_UPDATE;
break;
default:
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
}
else
- tag = "SELECT";
+ tag = CMDTAG_SELECT;
break;
case CMD_UPDATE:
- tag = "UPDATE";
+ tag = CMDTAG_UPDATE;
break;
case CMD_INSERT:
- tag = "INSERT";
+ tag = CMDTAG_INSERT;
break;
case CMD_DELETE:
- tag = "DELETE";
+ tag = CMDTAG_DELETE;
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
@@ -3093,7 +3083,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
}
@@ -3102,7 +3092,7 @@ CreateCommandTag(Node *parsetree)
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
- tag = "???";
+ tag = CMDTAG_UNKNOWN;
break;
}
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 1b63048a77..b9c1a0a5ad 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -20,6 +20,7 @@
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
+#include "tcop/cmdtag.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
@@ -51,7 +52,7 @@ static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD;
static void BuildEventTriggerCache(void);
static void InvalidateEventCacheCallback(Datum arg,
int cacheid, uint32 hashvalue);
-static int DecodeTextArrayToCString(Datum array, char ***cstringp);
+static Bitmapset *DecodeTextArrayToBitmapset(Datum array);
/*
* Search the event cache by trigger event.
@@ -180,10 +181,7 @@ BuildEventTriggerCache(void)
evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags,
RelationGetDescr(rel), &evttags_isnull);
if (!evttags_isnull)
- {
- item->ntags = DecodeTextArrayToCString(evttags, &item->tag);
- qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp);
- }
+ item->tagset = DecodeTextArrayToBitmapset(evttags);
/* Add to cache entry. */
entry = hash_search(cache, &event, HASH_ENTER, &found);
@@ -215,18 +213,18 @@ BuildEventTriggerCache(void)
}
/*
- * Decode text[] to an array of C strings.
+ * Decode text[] to a Bitmapset of CommandTags.
*
* We could avoid a bit of overhead here if we were willing to duplicate some
* of the logic from deconstruct_array, but it doesn't seem worth the code
* complexity.
*/
-static int
-DecodeTextArrayToCString(Datum array, char ***cstringp)
+static Bitmapset *
+DecodeTextArrayToBitmapset(Datum array)
{
ArrayType *arr = DatumGetArrayTypeP(array);
Datum *elems;
- char **cstring;
+ Bitmapset *bms;
int i;
int nelems;
@@ -234,13 +232,17 @@ DecodeTextArrayToCString(Datum array, char ***cstringp)
elog(ERROR, "expected 1-D text array");
deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems);
- cstring = palloc(nelems * sizeof(char *));
- for (i = 0; i < nelems; ++i)
- cstring[i] = TextDatumGetCString(elems[i]);
+ for (bms = NULL, i = 0; i < nelems; ++i)
+ {
+ char *str = TextDatumGetCString(elems[i]);
+
+ bms = bms_add_member(bms, GetCommandTagEnum(str));
+ pfree(str);
+ }
pfree(elems);
- *cstringp = cstring;
- return nelems;
+
+ return bms;
}
/*
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index c47be0ba4c..53401bd4e9 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -163,7 +163,7 @@ InitPlanCache(void)
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
MemoryContext source_context;
@@ -246,7 +246,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
CachedPlanSource *
CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ CommandTag commandTag)
{
CachedPlanSource *plansource;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index b675575c31..7072ce48a3 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -281,7 +281,7 @@ void
PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan)
{
@@ -289,10 +289,12 @@ PortalDefineQuery(Portal portal,
AssertState(portal->status == PORTAL_NEW);
AssertArg(sourceText != NULL);
- AssertArg(commandTag != NULL || stmts == NIL);
+ AssertArg(commandTag != CMDTAG_UNKNOWN || stmts == NIL);
portal->prepStmtName = prepStmtName;
portal->sourceText = sourceText;
+ portal->qc.commandTag = commandTag;
+ portal->qc.nprocessed = 0;
portal->commandTag = commandTag;
portal->stmts = stmts;
portal->cplan = cplan;
diff --git a/src/include/commands/createas.h b/src/include/commands/createas.h
index 7743851a38..5615b5ecac 100644
--- a/src/include/commands/createas.h
+++ b/src/include/commands/createas.h
@@ -22,7 +22,8 @@
extern ObjectAddress ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
- ParamListInfo params, QueryEnvironment *queryEnv, char *completionTag);
+ ParamListInfo params, QueryEnvironment *queryEnv,
+ QueryCompletion *qc);
extern int GetIntoRelEFlags(IntoClause *intoClause);
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index faa2958b89..28b352051b 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -17,6 +17,7 @@
#include "catalog/objectaddress.h"
#include "catalog/pg_event_trigger.h"
#include "nodes/parsenodes.h"
+#include "tcop/cmdtag.h"
#include "tcop/deparse_utility.h"
#include "utils/aclchk_internal.h"
@@ -25,7 +26,7 @@ typedef struct EventTriggerData
NodeTag type;
const char *event; /* event name */
Node *parsetree; /* parse tree */
- const char *tag; /* command tag */
+ CommandTag tag;
} EventTriggerData;
#define AT_REWRITE_ALTER_PERSISTENCE 0x01
diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h
index 6bdb7ca258..3ea4f5c80b 100644
--- a/src/include/commands/matview.h
+++ b/src/include/commands/matview.h
@@ -24,7 +24,7 @@
extern void SetMatViewPopulatedState(Relation relation, bool newstate);
extern ObjectAddress ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag);
+ ParamListInfo params, QueryCompletion *qc);
extern DestReceiver *CreateTransientRelDestReceiver(Oid oid);
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index 4ecc1a2ecd..5f64b0a674 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -23,7 +23,7 @@ extern void PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, Para
bool isTopLevel);
extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
- char *completionTag);
+ QueryCompletion *qc);
extern void PerformPortalClose(const char *name);
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index a0509e1f33..4fcf2406c1 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -40,7 +40,7 @@ extern void PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
extern void ExecuteQuery(ParseState *pstate,
ExecuteStmt *stmt, IntoClause *intoClause,
ParamListInfo params,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void DeallocateQuery(DeallocateStmt *stmt);
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into,
ExplainState *es, const char *queryString,
diff --git a/src/include/tcop/cmdtag.h b/src/include/tcop/cmdtag.h
new file mode 100644
index 0000000000..c164358c46
--- /dev/null
+++ b/src/include/tcop/cmdtag.h
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * cmdtag.h
+ * Declarations for commandtag names and enumeration.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/tcop/cmdtag.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CMDTAG_H
+#define CMDTAG_H
+
+
+#define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt) \
+ tag,
+
+typedef enum CommandTag
+{
+#include "tcop/cmdtaglist.h"
+ COMMAND_TAG_NEXTTAG
+} CommandTag;
+
+#undef PG_CMDTAG
+
+typedef struct QueryCompletion
+{
+ CommandTag commandTag;
+ uint64 nprocessed;
+} QueryCompletion;
+
+static inline void
+SetQueryCompletion(QueryCompletion *qc, CommandTag commandTag,
+ uint64 nprocessed)
+{
+ qc->commandTag = commandTag;
+ qc->nprocessed = nprocessed;
+}
+
+static inline void
+CopyQueryCompletion(QueryCompletion *dst, const QueryCompletion *src)
+{
+ dst->commandTag = src->commandTag;
+ dst->nprocessed = src->nprocessed;
+}
+
+extern void InitializeQueryCompletion(QueryCompletion *qc);
+extern const char *GetCommandTagName(CommandTag commandTag);
+extern bool command_tag_display_rowcount(CommandTag commandTag);
+extern bool command_tag_event_trigger_ok(CommandTag commandTag);
+extern bool command_tag_table_rewrite_ok(CommandTag commandTag);
+extern CommandTag GetCommandTagEnum(const char *tagname);
+
+#endif /* CMDTAG_H */
diff --git a/src/include/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h
new file mode 100644
index 0000000000..d28145a50d
--- /dev/null
+++ b/src/include/tcop/cmdtaglist.h
@@ -0,0 +1,218 @@
+/*----------------------------------------------------------------------
+ *
+ * cmdtaglist.h
+ * Command tags
+ *
+ * The command tag list is kept in its own source file for possible use
+ * by automatic tools. The exact representation of a command tag is
+ * determined by the PG_CMDTAG macro, which is not defined in this file;
+ * it can be defined by the caller for special purposes.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/tcop/cmdtaglist.h
+ *
+ *----------------------------------------------------------------------
+ */
+
+/* there is deliberately not an #ifndef CMDTAGLIST_H here */
+
+/*
+ * List of command tags. The entries must be sorted alphabetically on their
+ * textual name, so that we can bsearch on it; see GetCommandTagEnum().
+ */
+
+/* symbol name, textual name, event_trigger_ok, table_rewrite_ok, rowcount, last_oid */
+PG_CMDTAG(CMDTAG_UNKNOWN, "???", false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_ACCESS_METHOD, "ALTER ACCESS METHOD", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_AGGREGATE, "ALTER AGGREGATE", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_CAST, "ALTER CAST", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_COLLATION, "ALTER COLLATION", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_CONSTRAINT, "ALTER CONSTRAINT", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_CONVERSION, "ALTER CONVERSION", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_DATABASE, "ALTER DATABASE", false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_DEFAULT_PRIVILEGES, "ALTER DEFAULT PRIVILEGES", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_DOMAIN, "ALTER DOMAIN", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_EVENT_TRIGGER, "ALTER EVENT TRIGGER", false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_EXTENSION, "ALTER EXTENSION", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_FOREIGN_DATA_WRAPPER, "ALTER FOREIGN DATA WRAPPER", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_FOREIGN_TABLE, "ALTER FOREIGN TABLE", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_FUNCTION, "ALTER FUNCTION", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_INDEX, "ALTER INDEX", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_LANGUAGE, "ALTER LANGUAGE", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_LARGE_OBJECT, "ALTER LARGE OBJECT", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_MATERIALIZED_VIEW, "ALTER MATERIALIZED VIEW", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_OPERATOR, "ALTER OPERATOR", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_OPERATOR_CLASS, "ALTER OPERATOR CLASS", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_OPERATOR_FAMILY, "ALTER OPERATOR FAMILY", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_POLICY, "ALTER POLICY", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_PROCEDURE, "ALTER PROCEDURE", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_PUBLICATION, "ALTER PUBLICATION", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_ROLE, "ALTER ROLE", false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_ROUTINE, "ALTER ROUTINE", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_RULE, "ALTER RULE", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SCHEMA, "ALTER SCHEMA", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SEQUENCE, "ALTER SEQUENCE", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SERVER, "ALTER SERVER", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_STATISTICS, "ALTER STATISTICS", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SUBSCRIPTION, "ALTER SUBSCRIPTION", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SYSTEM, "ALTER SYSTEM", false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TABLE, "ALTER TABLE", true, true, false)
+PG_CMDTAG(CMDTAG_ALTER_TABLESPACE, "ALTER TABLESPACE", false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_CONFIGURATION, "ALTER TEXT SEARCH CONFIGURATION", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_DICTIONARY, "ALTER TEXT SEARCH DICTIONARY", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_PARSER, "ALTER TEXT SEARCH PARSER", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_TEMPLATE, "ALTER TEXT SEARCH TEMPLATE", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TRANSFORM, "ALTER TRANSFORM", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TRIGGER, "ALTER TRIGGER", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TYPE, "ALTER TYPE", true, true, false)
+PG_CMDTAG(CMDTAG_ALTER_USER_MAPPING, "ALTER USER MAPPING", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_VIEW, "ALTER VIEW", true, false, false)
+PG_CMDTAG(CMDTAG_ANALYZE, "ANALYZE", false, false, false)
+PG_CMDTAG(CMDTAG_BEGIN, "BEGIN", false, false, false)
+PG_CMDTAG(CMDTAG_CALL, "CALL", false, false, false)
+PG_CMDTAG(CMDTAG_CHECKPOINT, "CHECKPOINT", false, false, false)
+PG_CMDTAG(CMDTAG_CLOSE, "CLOSE", false, false, false)
+PG_CMDTAG(CMDTAG_CLOSE_CURSOR, "CLOSE CURSOR", false, false, false)
+PG_CMDTAG(CMDTAG_CLOSE_CURSOR_ALL, "CLOSE CURSOR ALL", false, false, false)
+PG_CMDTAG(CMDTAG_CLUSTER, "CLUSTER", false, false, false)
+PG_CMDTAG(CMDTAG_COMMENT, "COMMENT", true, false, false)
+PG_CMDTAG(CMDTAG_COMMIT, "COMMIT", false, false, false)
+PG_CMDTAG(CMDTAG_COMMIT_PREPARED, "COMMIT PREPARED", false, false, false)
+PG_CMDTAG(CMDTAG_COPY, "COPY", false, false, true)
+PG_CMDTAG(CMDTAG_COPY_FROM, "COPY FROM", false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_ACCESS_METHOD, "CREATE ACCESS METHOD", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_AGGREGATE, "CREATE AGGREGATE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_CAST, "CREATE CAST", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_COLLATION, "CREATE COLLATION", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_CONSTRAINT, "CREATE CONSTRAINT", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_CONVERSION, "CREATE CONVERSION", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_DATABASE, "CREATE DATABASE", false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_DOMAIN, "CREATE DOMAIN", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_EVENT_TRIGGER, "CREATE EVENT TRIGGER", false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_EXTENSION, "CREATE EXTENSION", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_FOREIGN_DATA_WRAPPER, "CREATE FOREIGN DATA WRAPPER", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_FOREIGN_TABLE, "CREATE FOREIGN TABLE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_FUNCTION, "CREATE FUNCTION", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_INDEX, "CREATE INDEX", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_LANGUAGE, "CREATE LANGUAGE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_MATERIALIZED_VIEW, "CREATE MATERIALIZED VIEW", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_OPERATOR, "CREATE OPERATOR", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_OPERATOR_CLASS, "CREATE OPERATOR CLASS", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_OPERATOR_FAMILY, "CREATE OPERATOR FAMILY", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_POLICY, "CREATE POLICY", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_PROCEDURE, "CREATE PROCEDURE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_PUBLICATION, "CREATE PUBLICATION", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_ROLE, "CREATE ROLE", false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_ROUTINE, "CREATE ROUTINE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_RULE, "CREATE RULE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_SCHEMA, "CREATE SCHEMA", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_SEQUENCE, "CREATE SEQUENCE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_SERVER, "CREATE SERVER", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_STATISTICS, "CREATE STATISTICS", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_SUBSCRIPTION, "CREATE SUBSCRIPTION", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TABLE, "CREATE TABLE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TABLE_AS, "CREATE TABLE AS", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TABLESPACE, "CREATE TABLESPACE", false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_CONFIGURATION, "CREATE TEXT SEARCH CONFIGURATION", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_DICTIONARY, "CREATE TEXT SEARCH DICTIONARY", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_PARSER, "CREATE TEXT SEARCH PARSER", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_TEMPLATE, "CREATE TEXT SEARCH TEMPLATE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TRANSFORM, "CREATE TRANSFORM", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TRIGGER, "CREATE TRIGGER", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TYPE, "CREATE TYPE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_USER_MAPPING, "CREATE USER MAPPING", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_VIEW, "CREATE VIEW", true, false, false)
+PG_CMDTAG(CMDTAG_DEALLOCATE, "DEALLOCATE", false, false, false)
+PG_CMDTAG(CMDTAG_DEALLOCATE_ALL, "DEALLOCATE ALL", false, false, false)
+PG_CMDTAG(CMDTAG_DECLARE_CURSOR, "DECLARE CURSOR", false, false, false)
+PG_CMDTAG(CMDTAG_DELETE, "DELETE", false, false, true)
+PG_CMDTAG(CMDTAG_DISCARD, "DISCARD", false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_ALL, "DISCARD ALL", false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_PLANS, "DISCARD PLANS", false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_SEQUENCES, "DISCARD SEQUENCES", false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_TEMP, "DISCARD TEMP", false, false, false)
+PG_CMDTAG(CMDTAG_DO, "DO", false, false, false)
+PG_CMDTAG(CMDTAG_DROP_ACCESS_METHOD, "DROP ACCESS METHOD", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_AGGREGATE, "DROP AGGREGATE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_CAST, "DROP CAST", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_COLLATION, "DROP COLLATION", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_CONSTRAINT, "DROP CONSTRAINT", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_CONVERSION, "DROP CONVERSION", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_DATABASE, "DROP DATABASE", false, false, false)
+PG_CMDTAG(CMDTAG_DROP_DOMAIN, "DROP DOMAIN", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_EVENT_TRIGGER, "DROP EVENT TRIGGER", false, false, false)
+PG_CMDTAG(CMDTAG_DROP_EXTENSION, "DROP EXTENSION", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_FOREIGN_DATA_WRAPPER, "DROP FOREIGN DATA WRAPPER", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_FOREIGN_TABLE, "DROP FOREIGN TABLE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_FUNCTION, "DROP FUNCTION", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_INDEX, "DROP INDEX", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_LANGUAGE, "DROP LANGUAGE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_MATERIALIZED_VIEW, "DROP MATERIALIZED VIEW", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_OPERATOR, "DROP OPERATOR", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_OPERATOR_CLASS, "DROP OPERATOR CLASS", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_OPERATOR_FAMILY, "DROP OPERATOR FAMILY", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_OWNED, "DROP OWNED", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_POLICY, "DROP POLICY", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_PROCEDURE, "DROP PROCEDURE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_PUBLICATION, "DROP PUBLICATION", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_REPLICATION_SLOT, "DROP REPLICATION SLOT", false, false, false)
+PG_CMDTAG(CMDTAG_DROP_ROLE, "DROP ROLE", false, false, false)
+PG_CMDTAG(CMDTAG_DROP_ROUTINE, "DROP ROUTINE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_RULE, "DROP RULE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_SCHEMA, "DROP SCHEMA", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_SEQUENCE, "DROP SEQUENCE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_SERVER, "DROP SERVER", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_STATISTICS, "DROP STATISTICS", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_SUBSCRIPTION, "DROP SUBSCRIPTION", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_TABLE, "DROP TABLE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_TABLESPACE, "DROP TABLESPACE", false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_CONFIGURATION, "DROP TEXT SEARCH CONFIGURATION", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_DICTIONARY, "DROP TEXT SEARCH DICTIONARY", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_PARSER, "DROP TEXT SEARCH PARSER", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_TEMPLATE, "DROP TEXT SEARCH TEMPLATE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_TRANSFORM, "DROP TRANSFORM", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_TRIGGER, "DROP TRIGGER", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_TYPE, "DROP TYPE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_USER_MAPPING, "DROP USER MAPPING", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_VIEW, "DROP VIEW", true, false, false)
+PG_CMDTAG(CMDTAG_EXECUTE, "EXECUTE", false, false, false)
+PG_CMDTAG(CMDTAG_EXPLAIN, "EXPLAIN", false, false, false)
+PG_CMDTAG(CMDTAG_FETCH, "FETCH", false, false, true)
+PG_CMDTAG(CMDTAG_GRANT, "GRANT", true, false, false)
+PG_CMDTAG(CMDTAG_GRANT_ROLE, "GRANT ROLE", false, false, false)
+PG_CMDTAG(CMDTAG_IMPORT_FOREIGN_SCHEMA, "IMPORT FOREIGN SCHEMA", true, false, false)
+PG_CMDTAG(CMDTAG_INSERT, "INSERT", false, false, true)
+PG_CMDTAG(CMDTAG_LISTEN, "LISTEN", false, false, false)
+PG_CMDTAG(CMDTAG_LOAD, "LOAD", false, false, false)
+PG_CMDTAG(CMDTAG_LOCK_TABLE, "LOCK TABLE", false, false, false)
+PG_CMDTAG(CMDTAG_MOVE, "MOVE", false, false, true)
+PG_CMDTAG(CMDTAG_NOTIFY, "NOTIFY", false, false, false)
+PG_CMDTAG(CMDTAG_PREPARE, "PREPARE", false, false, false)
+PG_CMDTAG(CMDTAG_PREPARE_TRANSACTION, "PREPARE TRANSACTION", false, false, false)
+PG_CMDTAG(CMDTAG_REASSIGN_OWNED, "REASSIGN OWNED", false, false, false)
+PG_CMDTAG(CMDTAG_REFRESH_MATERIALIZED_VIEW, "REFRESH MATERIALIZED VIEW", true, false, false)
+PG_CMDTAG(CMDTAG_REINDEX, "REINDEX", false, false, false)
+PG_CMDTAG(CMDTAG_RELEASE, "RELEASE", false, false, false)
+PG_CMDTAG(CMDTAG_RESET, "RESET", false, false, false)
+PG_CMDTAG(CMDTAG_REVOKE, "REVOKE", true, false, false)
+PG_CMDTAG(CMDTAG_REVOKE_ROLE, "REVOKE ROLE", false, false, false)
+PG_CMDTAG(CMDTAG_ROLLBACK, "ROLLBACK", false, false, false)
+PG_CMDTAG(CMDTAG_ROLLBACK_PREPARED, "ROLLBACK PREPARED", false, false, false)
+PG_CMDTAG(CMDTAG_SAVEPOINT, "SAVEPOINT", false, false, false)
+PG_CMDTAG(CMDTAG_SECURITY_LABEL, "SECURITY LABEL", true, false, false)
+PG_CMDTAG(CMDTAG_SELECT, "SELECT", false, false, true)
+PG_CMDTAG(CMDTAG_SELECT_FOR_KEY_SHARE, "SELECT FOR KEY SHARE", false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_NO_KEY_UPDATE, "SELECT FOR NO KEY UPDATE", false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_SHARE, "SELECT FOR SHARE", false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_UPDATE, "SELECT FOR UPDATE", false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_INTO, "SELECT INTO", true, false, false)
+PG_CMDTAG(CMDTAG_SET, "SET", false, false, false)
+PG_CMDTAG(CMDTAG_SET_CONSTRAINTS, "SET CONSTRAINTS", false, false, false)
+PG_CMDTAG(CMDTAG_SHOW, "SHOW", false, false, false)
+PG_CMDTAG(CMDTAG_START_TRANSACTION, "START TRANSACTION", false, false, false)
+PG_CMDTAG(CMDTAG_TRUNCATE_TABLE, "TRUNCATE TABLE", false, false, false)
+PG_CMDTAG(CMDTAG_UNLISTEN, "UNLISTEN", false, false, false)
+PG_CMDTAG(CMDTAG_UPDATE, "UPDATE", false, false, true)
+PG_CMDTAG(CMDTAG_VACUUM, "VACUUM", false, false, false)
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 35bce731a1..662ce8a56f 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -68,6 +68,7 @@
#define DEST_H
#include "executor/tuptable.h"
+#include "tcop/cmdtag.h"
/* buffer size to use for command completion tags */
@@ -134,9 +135,10 @@ extern PGDLLIMPORT DestReceiver *None_Receiver; /* permanent receiver for
/* The primary destination management functions */
-extern void BeginCommand(const char *commandTag, CommandDest dest);
+extern void BeginCommand(CommandTag commandTag, CommandDest dest);
extern DestReceiver *CreateDestReceiver(CommandDest dest);
-extern void EndCommand(const char *commandTag, CommandDest dest);
+extern void EndCommand(const QueryCompletion *qc, CommandDest dest,
+ bool force_undecorated_output);
/* Additional functions that go with destination management, more or less. */
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index 4ad6324e2d..437642cc72 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.h
@@ -35,7 +35,7 @@ extern void PortalSetResultFormat(Portal portal, int nFormats,
extern bool PortalRun(Portal portal, long count, bool isTopLevel,
bool run_once, DestReceiver *dest, DestReceiver *altdest,
- char *completionTag);
+ QueryCompletion *qc);
extern uint64 PortalRunFetch(Portal portal,
FetchDirection fdirection,
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index a551e08cb8..4aec19a008 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -14,6 +14,7 @@
#ifndef UTILITY_H
#define UTILITY_H
+#include "tcop/cmdtag.h"
#include "tcop/tcopprot.h"
typedef enum
@@ -71,17 +72,17 @@ typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
const char *queryString, ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, QueryCompletion *qc);
extern void ProcessUtilityForAlterTable(Node *stmt,
AlterTableUtilityContext *context);
@@ -92,7 +93,13 @@ extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
extern Query *UtilityContainsQuery(Node *parsetree);
-extern const char *CreateCommandTag(Node *parsetree);
+extern CommandTag CreateCommandTag(Node *parsetree);
+
+static inline const char *
+CreateCommandName(Node *parsetree)
+{
+ return GetCommandTagName(CreateCommandTag(parsetree));
+}
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index 6c3ff81ba3..bc8ce48061 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -28,8 +28,7 @@ typedef struct
{
Oid fnoid; /* function to be called */
char enabled; /* as SESSION_REPLICATION_ROLE_* */
- int ntags; /* number of command tags */
- char **tag; /* command tags in SORTED order */
+ Bitmapset *tagset; /* command tags, or NULL if empty */
} EventTriggerCacheItem;
extern List *EventCacheLookup(EventTriggerEvent event);
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index e48661ebec..6dde441586 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -18,6 +18,7 @@
#include "access/tupdesc.h"
#include "lib/ilist.h"
#include "nodes/params.h"
+#include "tcop/cmdtag.h"
#include "utils/queryenvironment.h"
/* Forward declaration, to avoid including parsenodes.h here */
@@ -95,7 +96,7 @@ typedef struct CachedPlanSource
int magic; /* should equal CACHEDPLANSOURCE_MAGIC */
struct RawStmt *raw_parse_tree; /* output of raw_parser(), or NULL */
const char *query_string; /* source text of query */
- const char *commandTag; /* command tag (a constant!), or NULL */
+ CommandTag commandTag;
Oid *param_types; /* array of parameter type OIDs, or NULL */
int num_params; /* length of param_types array */
ParserSetupHook parserSetup; /* alternative parameter spec method */
@@ -186,10 +187,10 @@ extern void ResetPlanCache(void);
extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ CommandTag commandTag);
extern void CompleteCachedPlan(CachedPlanSource *plansource,
List *querytree_list,
MemoryContext querytree_context,
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 0b69433722..d41ff2efda 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -48,6 +48,7 @@
#include "datatype/timestamp.h"
#include "executor/execdesc.h"
+#include "tcop/cmdtag.h"
#include "utils/plancache.h"
#include "utils/resowner.h"
@@ -132,7 +133,8 @@ typedef struct PortalData
/* The query or queries the portal will execute */
const char *sourceText; /* text of query (as of 8.4, never NULL) */
- const char *commandTag; /* command tag for original query */
+ CommandTag commandTag; /* command tag for original query */
+ QueryCompletion qc; /* command completion data for executed query */
List *stmts; /* list of PlannedStmts */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
@@ -227,7 +229,7 @@ extern Portal GetPortalByName(const char *name);
extern void PortalDefineQuery(Portal portal,
const char *prepStmtName,
const char *sourceText,
- const char *commandTag,
+ CommandTag commandTag,
List *stmts,
CachedPlan *cplan);
extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index f5de2332d5..a65bce0713 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1737,7 +1737,7 @@ plperl_event_trigger_build_args(FunctionCallInfo fcinfo)
tdata = (EventTriggerData *) fcinfo->context;
hv_store_string(hv, "event", cstr2sv(tdata->event));
- hv_store_string(hv, "tag", cstr2sv(tdata->tag));
+ hv_store_string(hv, "tag", cstr2sv(GetCommandTagName(tdata->tag)));
return newRV_noinc((SV *) hv);
}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 5acf604f63..a867c2c43b 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -37,6 +37,7 @@
#include "parser/scansup.h"
#include "plpgsql.h"
#include "storage/proc.h"
+#include "tcop/cmdtag.h"
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
#include "utils/array.h"
@@ -1473,7 +1474,7 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
case PLPGSQL_PROMISE_TG_TAG:
if (estate->evtrigdata == NULL)
elog(ERROR, "event trigger promise is not in an event trigger function");
- assign_text_var(estate, var, estate->evtrigdata->tag);
+ assign_text_var(estate, var, GetCommandTagName(estate->evtrigdata->tag));
break;
default:
@@ -4115,10 +4116,9 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
* tree(s), since those are the result of rewriting and could have
* been transmogrified into something else entirely.
*/
- if (plansource->commandTag &&
- (strcmp(plansource->commandTag, "INSERT") == 0 ||
- strcmp(plansource->commandTag, "UPDATE") == 0 ||
- strcmp(plansource->commandTag, "DELETE") == 0))
+ if (plansource->commandTag == CMDTAG_INSERT ||
+ plansource->commandTag == CMDTAG_UPDATE ||
+ plansource->commandTag == CMDTAG_DELETE)
{
stmt->mod_stmt = true;
break;
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index f0d170bec7..26e76f6a51 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -1329,7 +1329,8 @@ pltcl_event_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
Tcl_ListObjAppendElement(NULL, tcl_cmd,
Tcl_NewStringObj(utf_e2u(tdata->event), -1));
Tcl_ListObjAppendElement(NULL, tcl_cmd,
- Tcl_NewStringObj(utf_e2u(tdata->tag), -1));
+ Tcl_NewStringObj(utf_e2u(GetCommandTagName(tdata->tag)),
+ -1));
tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
diff --git a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
index e1629ec618..b7bdb88ce7 100644
--- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
+++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
@@ -74,7 +74,7 @@ get_command_tag(PG_FUNCTION_ARGS)
if (!cmd->parsetree)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(cstring_to_text(CreateCommandTag(cmd->parsetree)));
+ PG_RETURN_TEXT_P(cstring_to_text(CreateCommandName(cmd->parsetree)));
}
/*
--
2.20.1
On 2020-Mar-02, Alvaro Herrera wrote:
Here's the patch I propose for commit. I also rewrote the commit
message.
BTW I wonder if we should really change the definition of
EventTriggerData. ISTM that it would be sensible to keep it the same
for now ...
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Mar 2, 2020, at 9:08 AM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
On 2020-Mar-02, Alvaro Herrera wrote:
Here's the patch I propose for commit. I also rewrote the commit
message.BTW I wonder if we should really change the definition of
EventTriggerData. ISTM that it would be sensible to keep it the same
for now ...
I think it is more natural to change event trigger code to reason natively about CommandTags rather than continuing to reason about strings. The conversion back-and-forth between the enum and the string representation serves no useful purpose that I can see. But I understand if you are just trying to have the patch change fewer parts of the code, and if you feel more comfortable about reverting that part of the patch, as the committer, I think that's your prerogative.
Did you want to do that yourself, or have me do it and resubmit?
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 2020-Mar-02, Mark Dilger wrote:
I think it is more natural to change event trigger code to reason
natively about CommandTags rather than continuing to reason about
strings. The conversion back-and-forth between the enum and the
string representation serves no useful purpose that I can see. But I
understand if you are just trying to have the patch change fewer parts
of the code, and if you feel more comfortable about reverting that
part of the patch, as the committer, I think that's your prerogative.
Nah -- after reading it again, that would make no sense. With the
change, we're forcing all writers of event trigger functions in C to
adapt their functions to the new API, but that's okay -- I don't expect
there will be many, and we're doing other things to the API anyway.
I pushed it now.
I made very small changes before pushing: notably, I removed the
InitializeQueryCompletion() call from standard_ProcessUtility; instead
its callers are supposed to do it. Updated ProcessUtility's comment to
that effect.
Also, the affected plancache.c functions (CreateCachedPlan and
CreateOneShotCachedPlan) had not had their comments updated. Previously
they received compile-time constants, but that was important because
they were strings. No longer.
I noticed some other changes that could perhaps be made here, but didn't
do them; for instance, in pg_stat_statement we have a comparison to
CMDTAG_COPY that seems pointless; we could just acquire the value
always. I left it alone for now but I think the change is without side
effects (notably so because most actual DML does not go through
ProcessUtility anyway). Also, event_trigger.c could resolve command
strings to the tag enum earlier.
There's also a lot of nonsense in the pquery.c functions, such as this,
/*
* Now fetch desired portion of results.
*/
nprocessed = PortalRunSelect(portal, true, count, dest);
/*
* If the portal result contains a command tag and the caller
* gave us a pointer to store it, copy it and update the
* rowcount.
*/
if (qc && portal->qc.commandTag != CMDTAG_UNKNOWN)
{
CopyQueryCompletion(qc, &portal->qc);
qc->nprocessed = nprocessed;
}
I think we could simplify that by passing the qc.
Similar consideration with DoCopy; instead of a 'uint64 nprocessed' we
could have a *qc to fill in and avoid this bit of silliness,
DoCopy(pstate, (CopyStmt *) parsetree,
pstmt->stmt_location, pstmt->stmt_len,
&processed);
if (qc)
SetQueryCompletion(qc, CMDTAG_COPY, processed);
I see no reason to have PortalRun() initialize the qc; ISTM that its
callers should do so.
And so on.
Nothing of that is critical.
Thanks for your patch,
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Mar 2, 2020, at 1:57 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
On 2020-Mar-02, Mark Dilger wrote:
I think it is more natural to change event trigger code to reason
natively about CommandTags rather than continuing to reason about
strings. The conversion back-and-forth between the enum and the
string representation serves no useful purpose that I can see. But I
understand if you are just trying to have the patch change fewer parts
of the code, and if you feel more comfortable about reverting that
part of the patch, as the committer, I think that's your prerogative.Nah -- after reading it again, that would make no sense. With the
change, we're forcing all writers of event trigger functions in C to
adapt their functions to the new API, but that's okay -- I don't expect
there will be many, and we're doing other things to the API anyway.I pushed it now.
Thanks! I greatly appreciate your time.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Mar 2, 2020, at 1:57 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
I pushed it now.
Thanks again! While rebasing some other work on top, I noticed one of your comments is out of date:
--- a/src/include/tcop/cmdtaglist.h
+++ b/src/include/tcop/cmdtaglist.h
@@ -23,7 +23,7 @@
* textual name, so that we can bsearch on it; see GetCommandTagEnum().
*/
-/* symbol name, textual name, event_trigger_ok, table_rewrite_ok, rowcount, last_oid */
+/* symbol name, textual name, event_trigger_ok, table_rewrite_ok, rowcount */
PG_CMDTAG(CMDTAG_UNKNOWN, "???", false, false, false)
PG_CMDTAG(CMDTAG_ALTER_ACCESS_METHOD, "ALTER ACCESS METHOD", true, false, false)
PG_CMDTAG(CMDTAG_ALTER_AGGREGATE, "ALTER AGGREGATE", true, false, false)
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 2020-Mar-04, Mark Dilger wrote:
On Mar 2, 2020, at 1:57 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
I pushed it now.
Thanks again! While rebasing some other work on top, I noticed one of your comments is out of date:
--- a/src/include/tcop/cmdtaglist.h +++ b/src/include/tcop/cmdtaglist.h @@ -23,7 +23,7 @@ * textual name, so that we can bsearch on it; see GetCommandTagEnum(). */-/* symbol name, textual name, event_trigger_ok, table_rewrite_ok, rowcount, last_oid */ +/* symbol name, textual name, event_trigger_ok, table_rewrite_ok, rowcount */
Oops. Pushed, thanks.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services