提交 de28dc9a 编写于 作者: T Tom Lane

Portal and memory management infrastructure for extended query protocol.

Both plannable queries and utility commands are now always executed
within Portals, which have been revamped so that they can handle the
load (they used to be good only for single SELECT queries).  Restructure
code to push command-completion-tag selection logic out of postgres.c,
so that it won't have to be duplicated between simple and extended queries.
initdb forced due to addition of a field to Query nodes.
上级 1940434f
......@@ -77,7 +77,9 @@ PG_FUNCTION_INFO_V1(int_enum);
/*
* Manage the aggregation state of the array
* You need to specify the correct memory context, or it will vanish!
*
* Need to specify a suitably long-lived memory context, or it will vanish!
* PortalContext isn't really right, but it's close enough.
*/
static PGARRAY *
GetPGArray(int4 state, int fAdd)
......@@ -89,14 +91,7 @@ GetPGArray(int4 state, int fAdd)
/* New array */
int cb = PGARRAY_SIZE(START_NUM);
p = (PGARRAY *) MemoryContextAlloc(TopTransactionContext, cb);
if (!p)
{
elog(ERROR, "Integer aggregator, cant allocate TopTransactionContext memory");
return 0;
}
p = (PGARRAY *) MemoryContextAlloc(PortalContext, cb);
p->a.size = cb;
p->a.ndim = 0;
p->a.flags = 0;
......@@ -115,18 +110,6 @@ GetPGArray(int4 state, int fAdd)
int cbNew = PGARRAY_SIZE(n);
pn = (PGARRAY *) repalloc(p, cbNew);
if (!pn)
{ /* Realloc failed! Reallocate new block. */
pn = (PGARRAY *) MemoryContextAlloc(TopTransactionContext, cbNew);
if (!pn)
{
elog(ERROR, "Integer aggregator, REALLY REALLY can't alloc memory");
return (PGARRAY *) NULL;
}
memcpy(pn, p, p->a.size);
pfree(p);
}
pn->a.size = cbNew;
pn->lower = n;
return pn;
......@@ -149,24 +132,19 @@ ShrinkPGArray(PGARRAY * p)
/* use current transaction context */
pnew = palloc(cb);
if (pnew)
{
/*
* Fix up the fields in the new structure, so Postgres
* understands
*/
memcpy(pnew, p, cb);
pnew->a.size = cb;
pnew->a.ndim = 1;
pnew->a.flags = 0;
/*
* Fix up the fields in the new structure, so Postgres
* understands
*/
memcpy(pnew, p, cb);
pnew->a.size = cb;
pnew->a.ndim = 1;
pnew->a.flags = 0;
#ifndef PG_7_2
pnew->a.elemtype = INT4OID;
pnew->a.elemtype = INT4OID;
#endif
pnew->lower = 0;
}
else
elog(ERROR, "Integer aggregator, can't allocate memory");
pnew->lower = 0;
pfree(p);
}
return pnew;
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/postgres-ref.sgml,v 1.32 2003/03/25 16:15:43 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/postgres-ref.sgml,v 1.33 2003/05/02 20:54:33 tgl Exp $
PostgreSQL documentation
-->
......@@ -28,7 +28,6 @@ PostgreSQL documentation
<arg>-E</arg>
<arg>-f<group choice="plain"><arg>s</arg><arg>i</arg><arg>t</arg><arg>n</arg><arg>m</arg><arg>h</arg></group></arg>
<arg>-F</arg>
<arg>-i</arg>
<arg>-N</arg>
<arg>-o <replaceable>filename</replaceable></arg>
<arg>-O</arg>
......@@ -52,7 +51,6 @@ PostgreSQL documentation
<arg>-e</arg>
<arg>-f<group choice="plain"><arg>s</arg><arg>i</arg><arg>t</arg><arg>n</arg><arg>m</arg><arg>h</arg></group></arg>
<arg>-F</arg>
<arg>-i</arg>
<arg>-o <replaceable>filename</replaceable></arg>
<arg>-O</arg>
<arg>-p <replaceable>database</replaceable></arg>
......@@ -281,15 +279,6 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>-i</option></term>
<listitem>
<para>
Prevents query execution, but shows the plan tree.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-O</option></term>
<listitem>
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.146 2003/04/26 20:22:59 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.147 2003/05/02 20:54:33 tgl Exp $
*
* NOTES
* Transaction aborts can now occur two ways:
......@@ -49,7 +49,7 @@
* * CleanupTransaction() executes when we finally see a user COMMIT
* or ROLLBACK command; it cleans things up and gets us out of
* the transaction internally. In particular, we mustn't destroy
* TransactionCommandContext until this point.
* TopTransactionContext until this point.
*
* NOTES
* The essential aspects of the transaction system are:
......@@ -456,13 +456,12 @@ static void
AtStart_Memory(void)
{
/*
* We shouldn't have any transaction contexts already.
* We shouldn't have a transaction context already.
*/
Assert(TopTransactionContext == NULL);
Assert(TransactionCommandContext == NULL);
/*
* Create a toplevel context for the transaction.
* Create a toplevel context for the transaction, and make it active.
*/
TopTransactionContext =
AllocSetContextCreate(TopMemoryContext,
......@@ -471,16 +470,7 @@ AtStart_Memory(void)
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
/*
* Create a statement-level context and make it active.
*/
TransactionCommandContext =
AllocSetContextCreate(TopTransactionContext,
"TransactionCommandContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
MemoryContextSwitchTo(TransactionCommandContext);
MemoryContextSwitchTo(TopTransactionContext);
}
......@@ -659,7 +649,6 @@ AtCommit_Memory(void)
Assert(TopTransactionContext != NULL);
MemoryContextDelete(TopTransactionContext);
TopTransactionContext = NULL;
TransactionCommandContext = NULL;
}
/* ----------------------------------------------------------------
......@@ -769,19 +758,18 @@ AtAbort_Memory(void)
{
/*
* Make sure we are in a valid context (not a child of
* TransactionCommandContext...). Note that it is possible for this
* TopTransactionContext...). Note that it is possible for this
* code to be called when we aren't in a transaction at all; go
* directly to TopMemoryContext in that case.
*/
if (TransactionCommandContext != NULL)
if (TopTransactionContext != NULL)
{
MemoryContextSwitchTo(TransactionCommandContext);
MemoryContextSwitchTo(TopTransactionContext);
/*
* We do not want to destroy transaction contexts yet, but it
* should be OK to delete any command-local memory.
* We do not want to destroy the transaction's global state yet,
* so we can't free any memory here.
*/
MemoryContextResetAndDeleteChildren(TransactionCommandContext);
}
else
MemoryContextSwitchTo(TopMemoryContext);
......@@ -812,7 +800,6 @@ AtCleanup_Memory(void)
if (TopTransactionContext != NULL)
MemoryContextDelete(TopTransactionContext);
TopTransactionContext = NULL;
TransactionCommandContext = NULL;
}
......@@ -926,7 +913,7 @@ CommitTransaction(void)
* access, and in fact could still cause an error...)
*/
AtEOXact_portals(true);
AtCommit_Portals();
/* handle commit for large objects [ PA, 7/17/98 ] */
/* XXX probably this does not belong here */
......@@ -1057,7 +1044,7 @@ AbortTransaction(void)
* do abort processing
*/
DeferredTriggerAbortXact();
AtEOXact_portals(false);
AtAbort_Portals();
lo_commit(false); /* 'false' means it's abort */
AtAbort_Notify();
AtEOXact_UpdatePasswordFile(false);
......@@ -1126,7 +1113,8 @@ CleanupTransaction(void)
/*
* do abort cleanup processing
*/
AtCleanup_Memory();
AtCleanup_Portals(); /* now safe to release portal memory */
AtCleanup_Memory(); /* and transaction memory */
/*
* done with abort processing, set current transaction state back to
......@@ -1220,11 +1208,11 @@ StartTransactionCommand(bool preventChain)
}
/*
* We must switch to TransactionCommandContext before returning. This
* We must switch to TopTransactionContext before returning. This
* is already done if we called StartTransaction, otherwise not.
*/
Assert(TransactionCommandContext != NULL);
MemoryContextSwitchTo(TransactionCommandContext);
Assert(TopTransactionContext != NULL);
MemoryContextSwitchTo(TopTransactionContext);
}
/*
......@@ -1266,7 +1254,6 @@ CommitTransactionCommand(bool forceCommit)
Assert(s->blockState == TBLOCK_INPROGRESS);
/* This code must match the TBLOCK_INPROGRESS case below: */
CommandCounterIncrement();
MemoryContextResetAndDeleteChildren(TransactionCommandContext);
}
break;
......@@ -1283,15 +1270,10 @@ CommitTransactionCommand(bool forceCommit)
/*
* This is the case when we have finished executing a command
* someplace within a transaction block. We increment the
* command counter and return. Someday we may free resources
* local to the command.
*
* That someday is today, at least for memory allocated in
* TransactionCommandContext. - vadim 03/25/97
* command counter and return.
*/
case TBLOCK_INPROGRESS:
CommandCounterIncrement();
MemoryContextResetAndDeleteChildren(TransactionCommandContext);
break;
/*
......
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.107 2003/03/20 03:34:55 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.108 2003/05/02 20:54:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -189,10 +189,10 @@ cluster(ClusterStmt *stmt)
/*
* Create special memory context for cross-transaction storage.
*
* Since it is a child of QueryContext, it will go away even in case
* Since it is a child of PortalContext, it will go away even in case
* of error.
*/
cluster_context = AllocSetContextCreate(QueryContext,
cluster_context = AllocSetContextCreate(PortalContext,
"Cluster",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.97 2003/01/23 15:18:40 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.98 2003/05/02 20:54:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -720,11 +720,11 @@ ReindexDatabase(const char *dbname, bool force, bool all)
/*
* Create a memory context that will survive forced transaction
* commits we do below. Since it is a child of QueryContext, it will
* commits we do below. Since it is a child of PortalContext, it will
* go away eventually even if we suffer an error; there's no need for
* special abort cleanup logic.
*/
private_context = AllocSetContextCreate(QueryContext,
private_context = AllocSetContextCreate(PortalContext,
"ReindexDatabase",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
......
此差异已折叠。
......@@ -6,7 +6,7 @@
* Copyright (c) 2002, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.13 2003/02/02 23:46:38 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.14 2003/05/02 20:54:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -59,9 +59,8 @@ static ParamListInfo EvaluateParams(EState *estate,
void
PrepareQuery(PrepareStmt *stmt)
{
List *plan_list = NIL;
List *query_list,
*query_list_item;
*plan_list;
if (!stmt->name)
elog(ERROR, "No statement name given");
......@@ -69,19 +68,18 @@ PrepareQuery(PrepareStmt *stmt)
if (stmt->query->commandType == CMD_UTILITY)
elog(ERROR, "Utility statements cannot be prepared");
/*
* Parse analysis is already done, but we must still rewrite and plan
* the query.
*/
/* Rewrite the query. The result could be 0, 1, or many queries. */
query_list = QueryRewrite(stmt->query);
foreach(query_list_item, query_list)
{
Query *query = (Query *) lfirst(query_list_item);
Plan *plan;
plan = pg_plan_query(query);
plan_list = lappend(plan_list, plan);
}
/* Generate plans for queries. Snapshot is already set. */
plan_list = pg_plan_queries(query_list, false);
/* Save the results. */
StoreQuery(stmt->name, query_list, plan_list, stmt->argtype_oids);
}
......@@ -92,17 +90,19 @@ void
ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
{
QueryHashEntry *entry;
List *l,
*query_list,
List *query_list,
*plan_list;
MemoryContext qcontext;
ParamListInfo paramLI = NULL;
EState *estate = NULL;
Portal portal;
/* Look it up in the hash table */
entry = FetchQuery(stmt->name);
query_list = entry->query_list;
plan_list = entry->plan_list;
qcontext = entry->context;
Assert(length(query_list) == length(plan_list));
......@@ -117,57 +117,53 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list);
}
/* Execute each query */
foreach(l, query_list)
{
Query *query = (Query *) lfirst(l);
Plan *plan = (Plan *) lfirst(plan_list);
bool is_last_query;
plan_list = lnext(plan_list);
is_last_query = (plan_list == NIL);
if (query->commandType == CMD_UTILITY)
ProcessUtility(query->utilityStmt, outputDest, NULL);
else
{
QueryDesc *qdesc;
if (log_executor_stats)
ResetUsage();
/*
* Create a new portal to run the query in
*/
portal = CreateNewPortal();
qdesc = CreateQueryDesc(query, plan, outputDest, NULL,
paramLI, false);
/*
* For EXECUTE INTO, make a copy of the stored query so that we can
* modify its destination (yech, but INTO has always been ugly).
* For regular EXECUTE we can just use the stored query where it sits,
* since the executor is read-only.
*/
if (stmt->into)
{
MemoryContext oldContext;
Query *query;
if (stmt->into)
{
if (qdesc->operation != CMD_SELECT)
elog(ERROR, "INTO clause specified for non-SELECT query");
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
query->into = stmt->into;
qdesc->dest = None;
}
query_list = copyObject(query_list);
plan_list = copyObject(plan_list);
qcontext = PortalGetHeapMemory(portal);
ExecutorStart(qdesc);
if (length(query_list) != 1)
elog(ERROR, "INTO clause specified for non-SELECT query");
query = (Query *) lfirst(query_list);
if (query->commandType != CMD_SELECT)
elog(ERROR, "INTO clause specified for non-SELECT query");
query->into = copyObject(stmt->into);
ExecutorRun(qdesc, ForwardScanDirection, 0L);
MemoryContextSwitchTo(oldContext);
}
ExecutorEnd(qdesc);
PortalDefineQuery(portal,
NULL, /* XXX fixme: can we save query text? */
NULL, /* no command tag known either */
query_list,
plan_list,
qcontext);
FreeQueryDesc(qdesc);
/*
* Run the portal to completion.
*/
PortalStart(portal, paramLI);
if (log_executor_stats)
ShowUsage("EXECUTOR STATISTICS");
}
(void) PortalRun(portal, FETCH_ALL, outputDest, outputDest, NULL);
/*
* If we're processing multiple queries, we need to increment the
* command counter between them. For the last query, there's no
* need to do this, it's done automatically.
*/
if (!is_last_query)
CommandCounterIncrement();
}
PortalDrop(portal, false);
if (estate)
FreeExecutorState(estate);
......@@ -377,8 +373,11 @@ DeallocateQuery(DeallocateStmt *stmt)
/* Find the query's hash table entry */
entry = FetchQuery(stmt->name);
/* Flush the context holding the subsidiary data */
/* Drop any open portals that depend on this prepared statement */
Assert(MemoryContextIsValid(entry->context));
DropDependentPortals(entry->context);
/* Flush the context holding the subsidiary data */
MemoryContextDelete(entry->context);
/* Now we can remove the hash table entry */
......
......@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.251 2003/03/04 21:51:20 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.252 2003/05/02 20:54:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -189,11 +189,11 @@ vacuum(VacuumStmt *vacstmt)
/*
* Create special memory context for cross-transaction storage.
*
* Since it is a child of QueryContext, it will go away eventually even
* Since it is a child of PortalContext, it will go away eventually even
* if we suffer an error; there's no need for special abort cleanup
* logic.
*/
vac_context = AllocSetContextCreate(QueryContext,
vac_context = AllocSetContextCreate(PortalContext,
"Vacuum",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
......@@ -205,7 +205,7 @@ vacuum(VacuumStmt *vacstmt)
* lifetime.
*/
if (vacstmt->analyze && !vacstmt->vacuum)
anl_context = AllocSetContextCreate(QueryContext,
anl_context = AllocSetContextCreate(PortalContext,
"Analyze",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.128 2003/04/08 23:20:00 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.129 2003/05/02 20:54:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -377,7 +377,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
* XXX this is a horrid crock: since the pointer to the slot might live
* longer than the current evaluation context, we are forced to copy
* the tuple and slot into a long-lived context --- we use
* TransactionCommandContext which should be safe enough. This
* the econtext's per-query memory which should be safe enough. This
* represents a serious memory leak if many such tuples are processed
* in one command, however. We ought to redesign the representation
* of whole-tuple datums so that this is not necessary.
......@@ -391,7 +391,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
TupleTableSlot *tempSlot;
HeapTuple tup;
oldContext = MemoryContextSwitchTo(TransactionCommandContext);
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tempSlot = MakeTupleTableSlot();
tup = heap_copytuple(heapTuple);
ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.93 2003/04/29 22:13:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.94 2003/05/02 20:54:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -16,7 +16,6 @@
#include "access/printtup.h"
#include "catalog/heap.h"
#include "commands/portalcmds.h"
#include "executor/spi_priv.h"
#include "tcop/tcopprot.h"
#include "utils/lsyscache.h"
......@@ -91,7 +90,13 @@ SPI_connect(void)
_SPI_current->processed = 0;
_SPI_current->tuptable = NULL;
/* Create memory contexts for this procedure */
/*
* Create memory contexts for this procedure
*
* XXX it would be better to use PortalContext as the parent context,
* but we may not be inside a portal (consider deferred-trigger
* execution).
*/
_SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
"SPI Proc",
ALLOCSET_DEFAULT_MINSIZE,
......@@ -703,18 +708,14 @@ SPI_freetuptable(SPITupleTable *tuptable)
Portal
SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
{
static int unnamed_portal_count = 0;
_SPI_plan *spiplan = (_SPI_plan *) plan;
List *qtlist = spiplan->qtlist;
List *ptlist = spiplan->ptlist;
Query *queryTree;
Plan *planTree;
ParamListInfo paramLI;
QueryDesc *queryDesc;
MemoryContext oldcontext;
Portal portal;
char portalname[64];
int k;
/* Ensure that the plan contains only one regular SELECT query */
......@@ -737,32 +738,18 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
_SPI_current->processed = 0;
_SPI_current->tuptable = NULL;
if (name == NULL)
/* Create the portal */
if (name == NULL || name[0] == '\0')
{
/* Make up a portal name if none given */
for (;;)
{
unnamed_portal_count++;
if (unnamed_portal_count < 0)
unnamed_portal_count = 0;
sprintf(portalname, "<unnamed cursor %d>", unnamed_portal_count);
if (GetPortalByName(portalname) == NULL)
break;
}
name = portalname;
/* Use a random nonconflicting name */
portal = CreateNewPortal();
}
else
{
/* Ensure the portal doesn't exist already */
portal = GetPortalByName(name);
if (portal != NULL)
elog(ERROR, "cursor \"%s\" already in use", name);
/* In this path, error if portal of same name already exists */
portal = CreatePortal(name, false, false);
}
/* Create the portal */
portal = CreatePortal(name);
/* Switch to portals memory and copy the parsetree and plan to there */
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
queryTree = copyObject(queryTree);
......@@ -801,18 +788,33 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
else
paramLI = NULL;
/* Create the QueryDesc object */
queryDesc = CreateQueryDesc(queryTree, planTree, SPI, pstrdup(name),
paramLI, false);
/*
* Set up the portal.
*/
PortalDefineQuery(portal,
NULL, /* unfortunately don't have sourceText */
"SELECT", /* cursor's query is always a SELECT */
makeList1(queryTree),
makeList1(planTree),
PortalGetHeapMemory(portal));
/* Start the executor */
ExecutorStart(queryDesc);
MemoryContextSwitchTo(oldcontext);
/*
* Set up options for portal.
*/
portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
if (ExecSupportsBackwardScan(plan))
portal->cursorOptions |= CURSOR_OPT_SCROLL;
else
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
/* Arrange to shut down the executor if portal is dropped */
PortalSetQuery(portal, queryDesc);
/*
* Start portal execution.
*/
PortalStart(portal, paramLI);
/* Switch back to the callers memory context */
MemoryContextSwitchTo(oldcontext);
Assert(portal->strategy == PORTAL_ONE_SELECT);
/* Return the created portal */
return portal;
......@@ -1008,39 +1010,15 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
foreach(list_item, raw_parsetree_list)
{
Node *parsetree = (Node *) lfirst(list_item);
CmdType origCmdType;
bool foundOriginalQuery = false;
List *query_list;
List *query_list_item;
switch (nodeTag(parsetree))
{
case T_InsertStmt:
origCmdType = CMD_INSERT;
break;
case T_DeleteStmt:
origCmdType = CMD_DELETE;
break;
case T_UpdateStmt:
origCmdType = CMD_UPDATE;
break;
case T_SelectStmt:
origCmdType = CMD_SELECT;
break;
default:
/* Otherwise, never match commandType */
origCmdType = CMD_UNKNOWN;
break;
}
if (plan)
plan->origCmdType = origCmdType;
query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
query_list_list = lappend(query_list_list, query_list);
/* Reset state for each original parsetree */
/* (at most one of its querytrees will be marked canSetTag) */
SPI_processed = 0;
SPI_lastoid = InvalidOid;
SPI_tuptable = NULL;
......@@ -1050,39 +1028,11 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
{
Query *queryTree = (Query *) lfirst(query_list_item);
Plan *planTree;
bool canSetResult;
QueryDesc *qdesc;
planTree = pg_plan_query(queryTree);
plan_list = lappend(plan_list, planTree);
/*
* This query can set the SPI result if it is the original
* query, or if it is an INSTEAD query of the same kind as the
* original and we haven't yet seen the original query.
*/
if (queryTree->querySource == QSRC_ORIGINAL)
{
canSetResult = true;
foundOriginalQuery = true;
}
else if (!foundOriginalQuery &&
queryTree->commandType == origCmdType &&
(queryTree->querySource == QSRC_INSTEAD_RULE ||
queryTree->querySource == QSRC_QUAL_INSTEAD_RULE))
canSetResult = true;
else
canSetResult = false;
/* Reset state if can set result */
if (canSetResult)
{
SPI_processed = 0;
SPI_lastoid = InvalidOid;
SPI_tuptable = NULL;
_SPI_current->tuptable = NULL;
}
if (queryTree->commandType == CMD_UTILITY)
{
if (IsA(queryTree->utilityStmt, CopyStmt))
......@@ -1108,9 +1058,10 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
else if (plan == NULL)
{
qdesc = CreateQueryDesc(queryTree, planTree,
canSetResult ? SPI : None,
queryTree->canSetTag ? SPI : None,
NULL, NULL, false);
res = _SPI_pquery(qdesc, true, canSetResult ? tcount : 0);
res = _SPI_pquery(qdesc, true,
queryTree->canSetTag ? tcount : 0);
if (res < 0)
return res;
CommandCounterIncrement();
......@@ -1118,7 +1069,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
else
{
qdesc = CreateQueryDesc(queryTree, planTree,
canSetResult ? SPI : None,
queryTree->canSetTag ? SPI : None,
NULL, NULL, false);
res = _SPI_pquery(qdesc, false, 0);
if (res < 0)
......@@ -1145,10 +1096,31 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
List *query_list_list_item;
int nargs = plan->nargs;
int res = 0;
ParamListInfo paramLI;
/* Increment CommandCounter to see changes made by now */
CommandCounterIncrement();
/* Convert parameters to form wanted by executor */
if (nargs > 0)
{
int k;
paramLI = (ParamListInfo)
palloc0((nargs + 1) * sizeof(ParamListInfoData));
for (k = 0; k < nargs; k++)
{
paramLI[k].kind = PARAM_NUM;
paramLI[k].id = k + 1;
paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
paramLI[k].value = Values[k];
}
paramLI[k].kind = PARAM_INVALID;
}
else
paramLI = NULL;
/* Reset state (only needed in case string is empty) */
SPI_processed = 0;
SPI_lastoid = InvalidOid;
......@@ -1159,9 +1131,9 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
{
List *query_list = lfirst(query_list_list_item);
List *query_list_item;
bool foundOriginalQuery = false;
/* Reset state for each original parsetree */
/* (at most one of its querytrees will be marked canSetTag) */
SPI_processed = 0;
SPI_lastoid = InvalidOid;
SPI_tuptable = NULL;
......@@ -1171,72 +1143,24 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
{
Query *queryTree = (Query *) lfirst(query_list_item);
Plan *planTree;
bool canSetResult;
QueryDesc *qdesc;
planTree = lfirst(plan_list);
plan_list = lnext(plan_list);
/*
* This query can set the SPI result if it is the original
* query, or if it is an INSTEAD query of the same kind as the
* original and we haven't yet seen the original query.
*/
if (queryTree->querySource == QSRC_ORIGINAL)
{
canSetResult = true;
foundOriginalQuery = true;
}
else if (!foundOriginalQuery &&
queryTree->commandType == plan->origCmdType &&
(queryTree->querySource == QSRC_INSTEAD_RULE ||
queryTree->querySource == QSRC_QUAL_INSTEAD_RULE))
canSetResult = true;
else
canSetResult = false;
/* Reset state if can set result */
if (canSetResult)
{
SPI_processed = 0;
SPI_lastoid = InvalidOid;
SPI_tuptable = NULL;
_SPI_current->tuptable = NULL;
}
if (queryTree->commandType == CMD_UTILITY)
{
res = SPI_OK_UTILITY;
ProcessUtility(queryTree->utilityStmt, None, NULL);
res = SPI_OK_UTILITY;
CommandCounterIncrement();
}
else
{
ParamListInfo paramLI;
if (nargs > 0)
{
int k;
paramLI = (ParamListInfo)
palloc0((nargs + 1) * sizeof(ParamListInfoData));
for (k = 0; k < plan->nargs; k++)
{
paramLI[k].kind = PARAM_NUM;
paramLI[k].id = k + 1;
paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
paramLI[k].value = Values[k];
}
paramLI[k].kind = PARAM_INVALID;
}
else
paramLI = NULL;
qdesc = CreateQueryDesc(queryTree, planTree,
canSetResult ? SPI : None,
queryTree->canSetTag ? SPI : None,
NULL, paramLI, false);
res = _SPI_pquery(qdesc, true, canSetResult ? tcount : 0);
res = _SPI_pquery(qdesc, true,
queryTree->canSetTag ? tcount : 0);
if (res < 0)
return res;
CommandCounterIncrement();
......@@ -1346,10 +1270,10 @@ _SPI_cursor_operation(Portal portal, bool forward, int count,
/* Run the cursor */
_SPI_current->processed =
DoPortalFetch(portal,
forward ? FETCH_FORWARD : FETCH_BACKWARD,
(long) count,
dest);
PortalRunFetch(portal,
forward ? FETCH_FORWARD : FETCH_BACKWARD,
(long) count,
dest);
if (dest == SPI && _SPI_checktuples())
elog(FATAL, "SPI_fetch: # of processed tuples check failed");
......@@ -1467,7 +1391,6 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
}
else
newplan->argtypes = NULL;
newplan->origCmdType = plan->origCmdType;
MemoryContextSwitchTo(oldcxt);
......
......@@ -9,7 +9,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.2 2003/04/29 03:21:29 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.3 2003/05/02 20:54:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -43,18 +43,18 @@ tstoreSetupReceiver(DestReceiver *self, int operation,
const char *portalname, TupleDesc typeinfo)
{
TStoreState *myState = (TStoreState *) self;
Portal portal;
if (operation != CMD_SELECT)
elog(ERROR, "Unexpected operation type: %d", operation);
/* Should only be called within a suitably-prepped portal */
if (CurrentPortal == NULL ||
CurrentPortal->holdStore == NULL)
elog(ERROR, "Tuplestore destination used in wrong context");
portal = GetPortalByName(portalname);
/* Debug check: make sure portal's result tuple desc is correct */
Assert(CurrentPortal->tupDesc != NULL);
Assert(equalTupleDescs(CurrentPortal->tupDesc, typeinfo));
if (portal == NULL)
elog(ERROR, "Specified portal does not exist: %s", portalname);
myState->tstore = portal->holdStore;
myState->cxt = portal->holdContext;
myState->tstore = CurrentPortal->holdStore;
myState->cxt = CurrentPortal->holdContext;
}
/*
......
......@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.248 2003/04/08 23:20:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.249 2003/05/02 20:54:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1476,6 +1476,7 @@ _copyQuery(Query *from)
COPY_SCALAR_FIELD(commandType);
COPY_SCALAR_FIELD(querySource);
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
COPY_NODE_FIELD(into);
......
......@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.191 2003/04/08 23:20:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.192 2003/05/02 20:54:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -566,6 +566,7 @@ _equalQuery(Query *a, Query *b)
{
COMPARE_SCALAR_FIELD(commandType);
COMPARE_SCALAR_FIELD(querySource);
COMPARE_SCALAR_FIELD(canSetTag);
COMPARE_NODE_FIELD(utilityStmt);
COMPARE_SCALAR_FIELD(resultRelation);
COMPARE_NODE_FIELD(into);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.203 2003/04/24 21:16:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.204 2003/05/02 20:54:34 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
......@@ -1177,6 +1177,7 @@ _outQuery(StringInfo str, Query *node)
WRITE_ENUM_FIELD(commandType, CmdType);
WRITE_ENUM_FIELD(querySource, QuerySource);
WRITE_BOOL_FIELD(canSetTag);
/*
* Hack to work around missing outfuncs routines for a lot of the
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.151 2003/04/08 23:20:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.152 2003/05/02 20:54:34 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
......@@ -195,6 +195,7 @@ _readQuery(void)
READ_ENUM_FIELD(commandType, CmdType);
READ_ENUM_FIELD(querySource, QuerySource);
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
READ_NODE_FIELD(into);
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.61 2003/01/20 18:54:49 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.62 2003/05/02 20:54:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -65,10 +65,10 @@ geqo_eval(Query *root, List *initial_rels, Gene *tour, int num_gene)
*
* Since geqo_eval() will be called many times, we can't afford to let
* all that memory go unreclaimed until end of statement. Note we
* make the temp context a child of TransactionCommandContext, so that
* make the temp context a child of the planner's normal context, so that
* it will be freed even if we abort via elog(ERROR).
*/
mycontext = AllocSetContextCreate(TransactionCommandContext,
mycontext = AllocSetContextCreate(CurrentMemoryContext,
"GEQO",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.268 2003/04/29 22:13:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.269 2003/05/02 20:54:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -237,13 +237,23 @@ do_parse_analyze(Node *parseTree, ParseState *pstate)
/*
* Make sure that only the original query is marked original. We have
* to do this explicitly since recursive calls of do_parse_analyze will
* have marked some of the added-on queries as "original".
* have marked some of the added-on queries as "original". Also mark
* only the original query as allowed to set the command-result tag.
*/
foreach(listscan, result)
{
Query *q = lfirst(listscan);
q->querySource = (q == query ? QSRC_ORIGINAL : QSRC_PARSER);
if (q == query)
{
q->querySource = QSRC_ORIGINAL;
q->canSetTag = true;
}
else
{
q->querySource = QSRC_PARSER;
q->canSetTag = false;
}
}
return result;
......@@ -399,6 +409,11 @@ transformStmt(ParseState *pstate, Node *parseTree,
result->utilityStmt = (Node *) parseTree;
break;
}
/* Mark as original query until we learn differently */
result->querySource = QSRC_ORIGINAL;
result->canSetTag = true;
return result;
}
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.119 2003/04/29 22:13:10 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.120 2003/05/02 20:54:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1016,6 +1016,7 @@ fireRules(Query *parsetree,
event_qual, rt_index, event);
rule_action->querySource = qsrc;
rule_action->canSetTag = false; /* might change later */
results = lappend(results, rule_action);
}
......@@ -1181,6 +1182,9 @@ QueryRewrite(Query *parsetree)
List *querylist;
List *results = NIL;
List *l;
CmdType origCmdType;
bool foundOriginalQuery;
Query *lastInstead;
/*
* Step 1
......@@ -1235,5 +1239,51 @@ QueryRewrite(Query *parsetree)
results = lappend(results, query);
}
/*
* Step 3
*
* Determine which, if any, of the resulting queries is supposed to set
* the command-result tag; and update the canSetTag fields accordingly.
*
* If the original query is still in the list, it sets the command tag.
* Otherwise, the last INSTEAD query of the same kind as the original
* is allowed to set the tag. (Note these rules can leave us with no
* query setting the tag. The tcop code has to cope with this by
* setting up a default tag based on the original un-rewritten query.)
*
* The Asserts verify that at most one query in the result list is marked
* canSetTag. If we aren't checking asserts, we can fall out of the loop
* as soon as we find the original query.
*/
origCmdType = parsetree->commandType;
foundOriginalQuery = false;
lastInstead = NULL;
foreach(l, results)
{
Query *query = (Query *) lfirst(l);
if (query->querySource == QSRC_ORIGINAL)
{
Assert(query->canSetTag);
Assert(!foundOriginalQuery);
foundOriginalQuery = true;
#ifndef USE_ASSERT_CHECKING
break;
#endif
}
else
{
Assert(!query->canSetTag);
if (query->commandType == origCmdType &&
(query->querySource == QSRC_INSTEAD_RULE ||
query->querySource == QSRC_QUAL_INSTEAD_RULE))
lastInstead = query;
}
}
if (!foundOriginalQuery && lastInstead != NULL)
lastInstead->canSetTag = true;
return results;
}
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.59 2003/04/22 00:08:07 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.60 2003/05/02 20:54:35 tgl Exp $
*
* NOTES
* This cruft is the server side of PQfn.
......@@ -255,7 +255,7 @@ fetch_fp_info(Oid func_id, struct fp_info * fip)
*
* Note: palloc()s done here and in the called function do not need to be
* cleaned up explicitly. We are called from PostgresMain() in the
* QueryContext memory context, which will be automatically reset when
* MessageContext memory context, which will be automatically reset when
* control returns to PostgresMain.
*/
int
......
此差异已折叠。
此差异已折叠。
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.197 2003/03/20 18:52:48 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.198 2003/05/02 20:54:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1027,3 +1027,386 @@ ProcessUtility(Node *parsetree,
break;
}
}
/*
* CreateCommandTag
* utility to get a string representation of the
* command operation, given a raw (un-analyzed) parsetree.
*
* This must handle all raw command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
*
* NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
* Also, the result must point at a true constant (permanent storage).
*/
const char *
CreateCommandTag(Node *parsetree)
{
const char *tag;
switch (nodeTag(parsetree))
{
case T_InsertStmt:
tag = "INSERT";
break;
case T_DeleteStmt:
tag = "DELETE";
break;
case T_UpdateStmt:
tag = "UPDATE";
break;
case T_SelectStmt:
tag = "SELECT";
break;
case T_TransactionStmt:
{
TransactionStmt *stmt = (TransactionStmt *) parsetree;
switch (stmt->kind)
{
case TRANS_STMT_BEGIN:
tag = "BEGIN";
break;
case TRANS_STMT_START:
tag = "START TRANSACTION";
break;
case TRANS_STMT_COMMIT:
tag = "COMMIT";
break;
case TRANS_STMT_ROLLBACK:
tag = "ROLLBACK";
break;
default:
tag = "???";
break;
}
}
break;
case T_DeclareCursorStmt:
tag = "DECLARE CURSOR";
break;
case T_ClosePortalStmt:
tag = "CLOSE CURSOR";
break;
case T_FetchStmt:
{
FetchStmt *stmt = (FetchStmt *) parsetree;
tag = (stmt->ismove) ? "MOVE" : "FETCH";
}
break;
case T_CreateDomainStmt:
tag = "CREATE DOMAIN";
break;
case T_CreateSchemaStmt:
tag = "CREATE SCHEMA";
break;
case T_CreateStmt:
tag = "CREATE TABLE";
break;
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case DROP_TABLE:
tag = "DROP TABLE";
break;
case DROP_SEQUENCE:
tag = "DROP SEQUENCE";
break;
case DROP_VIEW:
tag = "DROP VIEW";
break;
case DROP_INDEX:
tag = "DROP INDEX";
break;
case DROP_TYPE:
tag = "DROP TYPE";
break;
case DROP_DOMAIN:
tag = "DROP DOMAIN";
break;
case DROP_CONVERSION:
tag = "DROP CONVERSION";
break;
case DROP_SCHEMA:
tag = "DROP SCHEMA";
break;
default:
tag = "???";
}
break;
case T_TruncateStmt:
tag = "TRUNCATE TABLE";
break;
case T_CommentStmt:
tag = "COMMENT";
break;
case T_CopyStmt:
tag = "COPY";
break;
case T_RenameStmt:
if (((RenameStmt *) parsetree)->renameType == RENAME_TRIGGER)
tag = "ALTER TRIGGER";
else
tag = "ALTER TABLE";
break;
case T_AlterTableStmt:
tag = "ALTER TABLE";
break;
case T_AlterDomainStmt:
tag = "ALTER DOMAIN";
break;
case T_GrantStmt:
{
GrantStmt *stmt = (GrantStmt *) parsetree;
tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
}
break;
case T_DefineStmt:
switch (((DefineStmt *) parsetree)->kind)
{
case DEFINE_STMT_AGGREGATE:
tag = "CREATE AGGREGATE";
break;
case DEFINE_STMT_OPERATOR:
tag = "CREATE OPERATOR";
break;
case DEFINE_STMT_TYPE:
tag = "CREATE TYPE";
break;
default:
tag = "???";
}
break;
case T_CompositeTypeStmt:
tag = "CREATE TYPE";
break;
case T_ViewStmt:
tag = "CREATE VIEW";
break;
case T_CreateFunctionStmt:
tag = "CREATE FUNCTION";
break;
case T_IndexStmt:
tag = "CREATE INDEX";
break;
case T_RuleStmt:
tag = "CREATE RULE";
break;
case T_CreateSeqStmt:
tag = "CREATE SEQUENCE";
break;
case T_AlterSeqStmt:
tag = "ALTER SEQUENCE";
break;
case T_RemoveAggrStmt:
tag = "DROP AGGREGATE";
break;
case T_RemoveFuncStmt:
tag = "DROP FUNCTION";
break;
case T_RemoveOperStmt:
tag = "DROP OPERATOR";
break;
case T_CreatedbStmt:
tag = "CREATE DATABASE";
break;
case T_AlterDatabaseSetStmt:
tag = "ALTER DATABASE";
break;
case T_DropdbStmt:
tag = "DROP DATABASE";
break;
case T_NotifyStmt:
tag = "NOTIFY";
break;
case T_ListenStmt:
tag = "LISTEN";
break;
case T_UnlistenStmt:
tag = "UNLISTEN";
break;
case T_LoadStmt:
tag = "LOAD";
break;
case T_ClusterStmt:
tag = "CLUSTER";
break;
case T_VacuumStmt:
if (((VacuumStmt *) parsetree)->vacuum)
tag = "VACUUM";
else
tag = "ANALYZE";
break;
case T_ExplainStmt:
tag = "EXPLAIN";
break;
case T_VariableSetStmt:
tag = "SET";
break;
case T_VariableShowStmt:
tag = "SHOW";
break;
case T_VariableResetStmt:
tag = "RESET";
break;
case T_CreateTrigStmt:
tag = "CREATE TRIGGER";
break;
case T_DropPropertyStmt:
switch (((DropPropertyStmt *) parsetree)->removeType)
{
case DROP_TRIGGER:
tag = "DROP TRIGGER";
break;
case DROP_RULE:
tag = "DROP RULE";
break;
default:
tag = "???";
}
break;
case T_CreatePLangStmt:
tag = "CREATE LANGUAGE";
break;
case T_DropPLangStmt:
tag = "DROP LANGUAGE";
break;
case T_CreateUserStmt:
tag = "CREATE USER";
break;
case T_AlterUserStmt:
tag = "ALTER USER";
break;
case T_AlterUserSetStmt:
tag = "ALTER USER";
break;
case T_DropUserStmt:
tag = "DROP USER";
break;
case T_LockStmt:
tag = "LOCK TABLE";
break;
case T_ConstraintsSetStmt:
tag = "SET CONSTRAINTS";
break;
case T_CreateGroupStmt:
tag = "CREATE GROUP";
break;
case T_AlterGroupStmt:
tag = "ALTER GROUP";
break;
case T_DropGroupStmt:
tag = "DROP GROUP";
break;
case T_CheckPointStmt:
tag = "CHECKPOINT";
break;
case T_ReindexStmt:
tag = "REINDEX";
break;
case T_CreateConversionStmt:
tag = "CREATE CONVERSION";
break;
case T_CreateCastStmt:
tag = "CREATE CAST";
break;
case T_DropCastStmt:
tag = "DROP CAST";
break;
case T_CreateOpClassStmt:
tag = "CREATE OPERATOR CLASS";
break;
case T_RemoveOpClassStmt:
tag = "DROP OPERATOR CLASS";
break;
case T_PrepareStmt:
tag = "PREPARE";
break;
case T_ExecuteStmt:
tag = "EXECUTE";
break;
case T_DeallocateStmt:
tag = "DEALLOCATE";
break;
default:
elog(LOG, "CreateCommandTag: unknown parse node type %d",
nodeTag(parsetree));
tag = "???";
break;
}
return tag;
}
......@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/mcxt.c,v 1.39 2003/03/27 16:51:29 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/mcxt.c,v 1.40 2003/05/02 20:54:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -43,9 +43,11 @@ MemoryContext TopMemoryContext = NULL;
MemoryContext ErrorContext = NULL;
MemoryContext PostmasterContext = NULL;
MemoryContext CacheMemoryContext = NULL;
MemoryContext QueryContext = NULL;
MemoryContext MessageContext = NULL;
MemoryContext TopTransactionContext = NULL;
MemoryContext TransactionCommandContext = NULL;
/* These two are transient links to contexts owned by other objects: */
MemoryContext QueryContext = NULL;
MemoryContext PortalContext = NULL;
/*****************************************************************************
......
/*-------------------------------------------------------------------------
*
* portalmem.c
* backend portal memory context management stuff
* backend portal memory management
*
* Portals are objects representing the execution state of a query.
* This module provides memory management services for portals, but it
* doesn't actually run the executor for them.
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.55 2003/04/29 03:21:29 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.56 2003/05/02 20:54:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* NOTES
* A "Portal" is a structure used to keep track of cursor queries.
*
* When the backend sees a "declare cursor" query, it allocates a
* "PortalData" structure, plans the query and then stores the query
* in the portal without executing it. Later, when the backend
* sees a
* fetch 1 from foo
* the system looks up the portal named "foo" in the portal table,
* gets the planned query and then calls the executor with a count
* of 1. The executor then runs the query and returns a single
* tuple. The problem is that we have to hold onto the state of the
* portal query until we see a "close". This means we have to be
* careful about memory management.
*
* I hope this makes things clearer to whoever reads this -cim 2/22/91
*/
#include "postgres.h"
#include "commands/portalcmds.h"
......@@ -51,6 +36,9 @@
* ----------------
*/
Portal CurrentPortal = NULL; /* globally visible pointer */
#define MAX_PORTALNAME_LEN NAMEDATALEN
typedef struct portalhashent
......@@ -66,7 +54,7 @@ do { \
PortalHashEnt *hentry; char key[MAX_PORTALNAME_LEN]; \
\
MemSet(key, 0, MAX_PORTALNAME_LEN); \
snprintf(key, MAX_PORTALNAME_LEN - 1, "%s", NAME); \
StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
key, HASH_FIND, NULL); \
if (hentry) \
......@@ -75,12 +63,12 @@ do { \
PORTAL = NULL; \
} while(0)
#define PortalHashTableInsert(PORTAL) \
#define PortalHashTableInsert(PORTAL, NAME) \
do { \
PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
\
MemSet(key, 0, MAX_PORTALNAME_LEN); \
snprintf(key, MAX_PORTALNAME_LEN - 1, "%s", PORTAL->name); \
StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
key, HASH_ENTER, &found); \
if (hentry == NULL) \
......@@ -88,6 +76,8 @@ do { \
if (found) \
elog(WARNING, "trying to insert a portal name that exists."); \
hentry->portal = PORTAL; \
/* To avoid duplicate storage, make PORTAL->name point to htab entry */ \
PORTAL->name = hentry->portalname; \
} while(0)
#define PortalHashTableDelete(PORTAL) \
......@@ -95,7 +85,7 @@ do { \
PortalHashEnt *hentry; char key[MAX_PORTALNAME_LEN]; \
\
MemSet(key, 0, MAX_PORTALNAME_LEN); \
snprintf(key, MAX_PORTALNAME_LEN - 1, "%s", PORTAL->name); \
StrNCpy(key, PORTAL->name, MAX_PORTALNAME_LEN); \
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
key, HASH_REMOVE, NULL); \
if (hentry == NULL) \
......@@ -155,50 +145,17 @@ GetPortalByName(const char *name)
return portal;
}
/*
* PortalSetQuery
* Attaches a QueryDesc to the specified portal. This should be
* called only after successfully doing ExecutorStart for the query.
*
* Note that in the case of DECLARE CURSOR, some Portal options have
* already been set in portalcmds.c's PreparePortal(). This is grotty.
*/
void
PortalSetQuery(Portal portal, QueryDesc *queryDesc)
{
AssertArg(PortalIsValid(portal));
/*
* If the user didn't specify a SCROLL type, allow or disallow
* scrolling based on whether it would require any additional
* runtime overhead to do so.
*/
if (portal->scrollType == DEFAULT_SCROLL)
{
if (ExecSupportsBackwardScan(queryDesc->plantree))
portal->scrollType = ENABLE_SCROLL;
else
portal->scrollType = DISABLE_SCROLL;
}
portal->queryDesc = queryDesc;
portal->executorRunning = true; /* now need to shut down executor */
portal->atStart = true;
portal->atEnd = false; /* allow fetches */
portal->portalPos = 0;
portal->posOverflow = false;
}
/*
* CreatePortal
* Returns a new portal given a name.
*
* An elog(WARNING) is emitted if portal name is in use (existing
* portal is returned!)
* allowDup: if true, automatically drop any pre-existing portal of the
* same name (if false, an error is raised).
*
* dupSilent: if true, don't even emit a WARNING.
*/
Portal
CreatePortal(const char *name)
CreatePortal(const char *name, bool allowDup, bool dupSilent)
{
Portal portal;
......@@ -207,43 +164,90 @@ CreatePortal(const char *name)
portal = GetPortalByName(name);
if (PortalIsValid(portal))
{
elog(WARNING, "CreatePortal: portal \"%s\" already exists", name);
return portal;
if (!allowDup)
elog(ERROR, "Portal \"%s\" already exists", name);
if (!dupSilent)
elog(WARNING, "Closing pre-existing portal \"%s\"", name);
PortalDrop(portal, false);
}
/* make new portal structure */
portal = (Portal) MemoryContextAlloc(PortalMemory, sizeof *portal);
portal = (Portal) MemoryContextAllocZero(PortalMemory, sizeof *portal);
/* initialize portal name */
portal->name = MemoryContextStrdup(PortalMemory, name);
/* initialize portal heap context */
/* initialize portal heap context; typically it won't store much */
portal->heap = AllocSetContextCreate(PortalMemory,
"PortalHeapMemory",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
ALLOCSET_SMALL_MINSIZE,
ALLOCSET_SMALL_INITSIZE,
ALLOCSET_SMALL_MAXSIZE);
/* initialize portal query */
portal->queryDesc = NULL;
/* initialize portal fields that don't start off zero */
portal->cleanup = PortalCleanup;
portal->scrollType = DEFAULT_SCROLL;
portal->executorRunning = false;
portal->holdOpen = false;
portal->createXact = GetCurrentTransactionId();
portal->holdStore = NULL;
portal->holdContext = NULL;
portal->strategy = PORTAL_MULTI_QUERY;
portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
portal->atStart = true;
portal->atEnd = true; /* disallow fetches until query is set */
portal->portalPos = 0;
portal->posOverflow = false;
/* put portal in table */
PortalHashTableInsert(portal);
/* put portal in table (sets portal->name) */
PortalHashTableInsert(portal, name);
return portal;
}
/*
* CreateNewPortal
* Create a new portal, assigning it a random nonconflicting name.
*/
Portal
CreateNewPortal(void)
{
static unsigned int unnamed_portal_count = 0;
char portalname[MAX_PORTALNAME_LEN];
/* Select a nonconflicting name */
for (;;)
{
unnamed_portal_count++;
sprintf(portalname, "<unnamed portal %u>", unnamed_portal_count);
if (GetPortalByName(portalname) == NULL)
break;
}
return CreatePortal(portalname, false, false);
}
/*
* PortalDefineQuery
* A simple subroutine to establish a portal's query.
*
* Notes: the passed commandTag must be a pointer to a constant string,
* since it is not copied. The caller is responsible for ensuring that
* the passed sourceText (if any), parse and plan trees have adequate
* lifetime. Also, queryContext must accurately describe the location
* of the parse and plan trees.
*/
void
PortalDefineQuery(Portal portal,
const char *sourceText,
const char *commandTag,
List *parseTrees,
List *planTrees,
MemoryContext queryContext)
{
AssertArg(PortalIsValid(portal));
AssertState(portal->queryContext == NULL); /* else defined already */
Assert(length(parseTrees) == length(planTrees));
portal->sourceText = sourceText;
portal->commandTag = commandTag;
portal->parseTrees = parseTrees;
portal->planTrees = planTrees;
portal->queryContext = queryContext;
}
/*
* PortalDrop
* Destroy the portal.
......@@ -256,6 +260,10 @@ PortalDrop(Portal portal, bool isError)
{
AssertArg(PortalIsValid(portal));
/* Not sure if this case can validly happen or not... */
if (portal->portalActive)
elog(ERROR, "PortalDrop: can't drop active portal");
/*
* Remove portal from hash table. Because we do this first, we will
* not come back to try to remove the portal again if there's any error
......@@ -273,21 +281,43 @@ PortalDrop(Portal portal, bool isError)
MemoryContextDelete(portal->holdContext);
/* release subsidiary storage */
if (PortalGetHeapMemory(portal))
MemoryContextDelete(PortalGetHeapMemory(portal));
MemoryContextDelete(PortalGetHeapMemory(portal));
/* release name and portal data (both are in PortalMemory) */
pfree(portal->name);
/* release portal struct (it's in PortalMemory) */
pfree(portal);
}
/*
* Cleanup the portals created in the current transaction. If the
* transaction was aborted, all the portals created in this transaction
* should be removed. If the transaction was successfully committed, any
* holdable cursors created in this transaction need to be kept
* open. In any case, portals remaining from prior transactions should
* be left untouched.
* DropDependentPortals
* Drop any portals using the specified context as queryContext.
*
* This is normally used to make sure we can safely drop a prepared statement.
*/
void
DropDependentPortals(MemoryContext queryContext)
{
HASH_SEQ_STATUS status;
PortalHashEnt *hentry;
hash_seq_init(&status, PortalHashTable);
while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
{
Portal portal = hentry->portal;
if (portal->queryContext == queryContext)
PortalDrop(portal, false);
}
}
/*
* Pre-commit processing for portals.
*
* Any holdable cursors created in this transaction need to be converted to
* materialized form, since we are going to close down the executor and
* release locks. Remove all other portals created in this transaction.
* Portals remaining from prior transactions should be left untouched.
*
* XXX This assumes that portals can be deleted in a random order, ie,
* no portal has a reference to any other (at least not one that will be
......@@ -296,7 +326,7 @@ PortalDrop(Portal portal, bool isError)
* references...
*/
void
AtEOXact_portals(bool isCommit)
AtCommit_Portals(void)
{
HASH_SEQ_STATUS status;
PortalHashEnt *hentry;
......@@ -308,11 +338,21 @@ AtEOXact_portals(bool isCommit)
{
Portal portal = hentry->portal;
if (portal->createXact != xact)
/*
* Do not touch active portals --- this can only happen in the case of
* a multi-transaction utility command, such as VACUUM.
*/
if (portal->portalActive)
continue;
if (portal->holdOpen && isCommit)
if (portal->cursorOptions & CURSOR_OPT_HOLD)
{
/*
* Do nothing to cursors held over from a previous transaction.
*/
if (portal->createXact != xact)
continue;
/*
* We are exiting the transaction that created a holdable
* cursor. Instead of dropping the portal, prepare it for
......@@ -321,7 +361,8 @@ AtEOXact_portals(bool isCommit)
/*
* Create the memory context that is used for storage of
* the held cursor's tuple set.
* the held cursor's tuple set. Note this is NOT a child
* of the portal's heap memory.
*/
portal->holdContext =
AllocSetContextCreate(PortalMemory,
......@@ -341,7 +382,91 @@ AtEOXact_portals(bool isCommit)
}
else
{
PortalDrop(portal, !isCommit);
/* Zap all non-holdable portals */
PortalDrop(portal, false);
}
}
}
/*
* Abort processing for portals.
*
* At this point we reset the "active" flags and run the cleanup hook if
* present, but we can't release memory until the cleanup call.
*
* The reason we need to reset active is so that we can replace the unnamed
* portal, else we'll fail to execute ROLLBACK when it arrives. Also, we
* want to run the cleanup hook now to be certain it knows that we had an
* error abort and not successful conclusion.
*/
void
AtAbort_Portals(void)
{
HASH_SEQ_STATUS status;
PortalHashEnt *hentry;
TransactionId xact = GetCurrentTransactionId();
hash_seq_init(&status, PortalHashTable);
while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
{
Portal portal = hentry->portal;
portal->portalActive = false;
/*
* Do nothing else to cursors held over from a previous transaction.
* (This test must include checking CURSOR_OPT_HOLD, else we will
* fail to clean up a VACUUM portal if it fails after its first
* sub-transaction.)
*/
if (portal->createXact != xact &&
(portal->cursorOptions & CURSOR_OPT_HOLD))
continue;
/* let portalcmds.c clean up the state it knows about */
if (PointerIsValid(portal->cleanup))
{
(*portal->cleanup) (portal, true);
portal->cleanup = NULL;
}
}
}
/*
* Post-abort cleanup for portals.
*
* Delete all portals not held over from prior transactions.
*/
void
AtCleanup_Portals(void)
{
HASH_SEQ_STATUS status;
PortalHashEnt *hentry;
TransactionId xact = GetCurrentTransactionId();
hash_seq_init(&status, PortalHashTable);
while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
{
Portal portal = hentry->portal;
/*
* Let's just make sure no one's active...
*/
portal->portalActive = false;
/*
* Do nothing else to cursors held over from a previous transaction.
* (This test must include checking CURSOR_OPT_HOLD, else we will
* fail to clean up a VACUUM portal if it fails after its first
* sub-transaction.)
*/
if (portal->createXact != xact &&
(portal->cursorOptions & CURSOR_OPT_HOLD))
continue;
/* Else zap it with prejudice. */
PortalDrop(portal, true);
}
}
......@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.184 2003/04/08 23:20:02 tgl Exp $
* $Id: catversion.h,v 1.185 2003/05/02 20:54:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200304071
#define CATALOG_VERSION_NO 200305011
#endif
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: portalcmds.h,v 1.7 2003/04/29 03:21:30 tgl Exp $
* $Id: portalcmds.h,v 1.8 2003/05/02 20:54:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -22,13 +22,10 @@ extern void PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest);
extern void PerformPortalFetch(FetchStmt *stmt, CommandDest dest,
char *completionTag);
extern long DoPortalFetch(Portal portal,
FetchDirection fdirection,
long count,
CommandDest dest);
extern void PerformPortalClose(char *name);
extern void PortalCleanup(Portal portal, bool isError);
extern void PersistHoldablePortal(Portal portal);
#endif /* PORTALCMDS_H */
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: spi_priv.h,v 1.13 2002/10/14 23:49:20 tgl Exp $
* $Id: spi_priv.h,v 1.14 2003/05/02 20:54:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -36,8 +36,6 @@ typedef struct
/* Argument types, if a prepared plan */
int nargs;
Oid *argtypes;
/* Command type of last original parsetree */
CmdType origCmdType;
} _SPI_plan;
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.236 2003/03/27 16:51:29 momjian Exp $
* $Id: parsenodes.h,v 1.237 2003/05/02 20:54:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -47,6 +47,8 @@ typedef struct Query
QuerySource querySource; /* where did I come from? */
bool canSetTag; /* do I set the command result tag? */
Node *utilityStmt; /* non-null if this is a non-optimizable
* statement */
......
......@@ -7,17 +7,32 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pquery.h,v 1.24 2003/03/10 03:53:52 tgl Exp $
* $Id: pquery.h,v 1.25 2003/05/02 20:54:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PQUERY_H
#define PQUERY_H
#include "executor/execdesc.h"
#include "utils/portal.h"
extern void ProcessQuery(Query *parsetree, Plan *plan, CommandDest dest,
char *completionTag);
extern void ProcessQuery(Query *parsetree,
Plan *plan,
ParamListInfo params,
const char *portalName,
CommandDest dest,
char *completionTag);
extern void PortalStart(Portal portal, ParamListInfo params);
extern bool PortalRun(Portal portal, long count,
CommandDest dest, CommandDest altdest,
char *completionTag);
extern long PortalRunFetch(Portal portal,
FetchDirection fdirection,
long count,
CommandDest dest);
#endif /* PQUERY_H */
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: tcopprot.h,v 1.55 2003/04/29 22:13:11 tgl Exp $
* $Id: tcopprot.h,v 1.56 2003/05/02 20:54:36 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
......@@ -41,6 +41,7 @@ extern List *pg_analyze_and_rewrite(Node *parsetree,
extern List *pg_parse_and_rewrite(const char *query_string,
Oid *paramTypes, int numParams);
extern Plan *pg_plan_query(Query *querytree);
extern List *pg_plan_queries(List *querytrees, bool needSnapshot);
#endif /* BOOTSTRAP_INCLUDE */
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: utility.h,v 1.16 2002/09/04 20:31:45 momjian Exp $
* $Id: utility.h,v 1.17 2003/05/02 20:54:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -19,4 +19,6 @@
extern void ProcessUtility(Node *parsetree, CommandDest dest,
char *completionTag);
extern const char *CreateCommandTag(Node *parsetree);
#endif /* UTILITY_H */
......@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: memutils.h,v 1.50 2002/12/16 16:22:46 tgl Exp $
* $Id: memutils.h,v 1.51 2003/05/02 20:54:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -68,9 +68,11 @@ extern DLLIMPORT MemoryContext TopMemoryContext;
extern DLLIMPORT MemoryContext ErrorContext;
extern DLLIMPORT MemoryContext PostmasterContext;
extern DLLIMPORT MemoryContext CacheMemoryContext;
extern DLLIMPORT MemoryContext QueryContext;
extern DLLIMPORT MemoryContext MessageContext;
extern DLLIMPORT MemoryContext TopTransactionContext;
extern DLLIMPORT MemoryContext TransactionCommandContext;
/* These two are transient links to contexts owned by other objects: */
extern DLLIMPORT MemoryContext QueryContext;
extern DLLIMPORT MemoryContext PortalContext;
/*
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册