1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-24 00:23:06 +03:00

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
This commit is contained in:
Alvaro Herrera
2020-03-02 18:19:51 -03:00
parent 7b425a5283
commit 2f9661311b
39 changed files with 877 additions and 621 deletions

View File

@@ -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);
}
}