Skip to content

Commit

Permalink
Represent command completion tags as structs
Browse files Browse the repository at this point in the history
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
  • Loading branch information
alvherre committed Mar 2, 2020
1 parent 7b425a5 commit 2f96613
Show file tree
Hide file tree
Showing 39 changed files with 877 additions and 621 deletions.
20 changes: 8 additions & 12 deletions contrib/pg_stat_statements/pg_stat_statements.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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();
{
Expand All @@ -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 =
Expand Down Expand Up @@ -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);
}
}

Expand Down
6 changes: 3 additions & 3 deletions contrib/sepgsql/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
{
Expand Down
2 changes: 1 addition & 1 deletion doc/src/sgml/event-trigger.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
14 changes: 6 additions & 8 deletions src/backend/commands/createas.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 2f96613

Please sign in to comment.