mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +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:
@@ -13,6 +13,7 @@ top_builddir = ../../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
|
||||
OBJS = \
|
||||
cmdtag.o \
|
||||
dest.o \
|
||||
fastpath.o \
|
||||
postgres.o \
|
||||
|
98
src/backend/tcop/cmdtag.c
Normal file
98
src/backend/tcop/cmdtag.c
Normal file
@@ -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;
|
||||
}
|
@@ -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 (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);
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user