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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user