Commit 2f966131 authored by Alvaro Herrera's avatar Alvaro Herrera

Represent command completion tags as structs

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-definable 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, with unsolicited help from Álvaro Herrera
Reviewed-by: John Naylor, Tom Lane
Discussion: https://postgr.es/m/981A9DB4-3F0C-4DA5-88AD-CB9CFF4D6CAD@enterprisedb.com
parent 7b425a52
......@@ -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,12 +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);
else
rows = 0;
if (qc)
rows = qc->commandTag == CMDTAG_COPY ? qc->nprocessed : 0;
/* calc differences of buffer counters. */
bufusage.shared_blks_hit =
......@@ -1060,11 +1056,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);
}
}
......
......@@ -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();
{
......
......@@ -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>
......
......@@ -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;
......
......@@ -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 */
......
......@@ -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;
......
......@@ -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,9 +202,8 @@ PerformPortalFetch(FetchStmt *stmt,
dest);
/* Return command status if wanted */
if (completionTag)
snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT,
stmt->ismove ? "MOVE" : "FETCH",
if (qc)
SetQueryCompletion(qc, stmt->ismove ? CMDTAG_MOVE : CMDTAG_FETCH,
nprocessed);
}
......
......@@ -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);
......
......@@ -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));
}
......
......@@ -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));
......
......@@ -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,8 +2291,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else
{
char completionTag[COMPLETION_TAG_BUFSIZE];
ProcessUtilityContext context;
QueryCompletion qc;
/*
* If the SPI context is atomic, or we are asked to manage
......@@ -2306,13 +2306,14 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
else
context = PROCESS_UTILITY_QUERY_NONATOMIC;
InitializeQueryCompletion(&qc);
ProcessUtility(stmt,
plansource->query_string,
context,
paramLI,
_SPI_current->queryEnv,
dest,
completionTag);
&qc);
/* Update "processed" if stmt returned tuples */
if (_SPI_current->tuptable)
......@@ -2328,9 +2329,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 +2351,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;
}
}
......
......@@ -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
......
......@@ -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);
......
......@@ -13,6 +13,7 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = \
cmdtag.o \
dest.o \
fastpath.o \
postgres.o \
......
/*-------------------------------------------------------------------------
*
* 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;
}
......@@ -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:
......
......@@ -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,6 @@ exec_parse_message(const char *query_string, /* string to execute */
MemoryContext oldcontext;
List *parsetree_list;
RawStmt *raw_parse_tree;
const char *commandTag;
List *querytree_list;
CachedPlanSource *psrc;
bool is_named;
......@@ -1438,11 +1437,6 @@ exec_parse_message(const char *query_string, /* string to execute */
raw_parse_tree = linitial_node(RawStmt, parsetree_list);
/*
* Get the command name for possible use in status display.
*/
commandTag = CreateCommandTag(raw_parse_tree->stmt);
/*
* If we are in an aborted transaction, reject all commands except
* COMMIT/ROLLBACK. It is important that this test occur before we
......@@ -1463,7 +1457,8 @@ exec_parse_message(const char *query_string, /* string to execute */
* Create the CachedPlanSource before we do parse analysis, since it
* needs to see the unmodified raw parse tree.
*/
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
psrc = CreateCachedPlan(raw_parse_tree, query_string,
CreateCommandTag(raw_parse_tree->stmt));
/*
* Set up a snapshot if parse analysis will need one.
......@@ -1514,8 +1509,8 @@ exec_parse_message(const char *query_string, /* string to execute */
{
/* Empty input string. This is legal. */
raw_parse_tree = NULL;
commandTag = NULL;
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
psrc = CreateCachedPlan(raw_parse_tree, query_string,
CMDTAG_UNKNOWN);
querytree_list = NIL;
}
......@@ -2031,7 +2026,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 +2053,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 +2099,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 +2180,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 +2213,7 @@ exec_execute_message(const char *portal_name, long max_rows)
}
/* Send appropriate CommandComplete to client */
EndCommand(completionTag, dest);
EndCommand(&qc, dest, false);
}
else
{
......
......@@ -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 (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 (qc && qc->commandTag == CMDTAG_UNKNOWN)
{
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);
}
}
......
This diff is collapsed.
......@@ -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;
}
/*
......
......@@ -158,12 +158,12 @@ InitPlanCache(void)
*
* raw_parse_tree: output of raw_parser(), or NULL if empty query
* query_string: original query text
* commandTag: compile-time-constant tag for query, or NULL if empty query
* commandTag: command tag for query, or UNKNOWN if empty query
*/
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
const char *commandTag)
CommandTag commandTag)
{
CachedPlanSource *plansource;
MemoryContext source_context;
......@@ -241,12 +241,12 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
*
* raw_parse_tree: output of raw_parser(), or NULL if empty query
* query_string: original query text
* commandTag: compile-time-constant tag for query, or NULL if empty query
* commandTag: command tag for query, or NULL if empty query
*/
CachedPlanSource *
CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
const char *commandTag)
CommandTag commandTag)
{
CachedPlanSource *plansource;
......
......@@ -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;
......
......@@ -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);
......
......@@ -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
......
......@@ -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);
......
......@@ -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);
......
......@@ -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,
......
/*-------------------------------------------------------------------------
*
* 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 */
This diff is collapsed.
......@@ -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. */
......
......@@ -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,
......
......@@ -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);
......
......@@ -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);
......
......@@ -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; /* 'nuff said */
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,
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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;
......
......@@ -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));
......
......@@ -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)));
}
/*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment