mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
Redesign query-snapshot timing so that volatile functions in READ COMMITTED
mode see a fresh snapshot for each command in the function, rather than using the latest interactive command's snapshot. Also, suppress fresh snapshots as well as CommandCounterIncrement inside STABLE and IMMUTABLE functions, instead using the snapshot taken for the most closely nested regular query. (This behavior is only sane for read-only functions, so the patch also enforces that such functions contain only SELECT commands.) As per my proposal of 6-Sep-2004; I note that I floated essentially the same proposal on 19-Jun-2002, but that discussion tailed off without any action. Since 8.0 seems like the right place to be taking possibly nontrivial backwards compatibility hits, let's get it done now.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.86 2004/09/10 18:40:00 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.87 2004/09/13 20:07:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -32,6 +32,11 @@
|
||||
Portal ActivePortal = NULL;
|
||||
|
||||
|
||||
static void ProcessQuery(Query *parsetree,
|
||||
Plan *plan,
|
||||
ParamListInfo params,
|
||||
DestReceiver *dest,
|
||||
char *completionTag);
|
||||
static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
|
||||
DestReceiver *dest);
|
||||
static long PortalRunSelect(Portal portal, bool forward, long count,
|
||||
@ -54,6 +59,8 @@ static void DoPortalRewind(Portal portal);
|
||||
QueryDesc *
|
||||
CreateQueryDesc(Query *parsetree,
|
||||
Plan *plantree,
|
||||
Snapshot snapshot,
|
||||
Snapshot crosscheck_snapshot,
|
||||
DestReceiver *dest,
|
||||
ParamListInfo params,
|
||||
bool doInstrument)
|
||||
@ -63,6 +70,8 @@ CreateQueryDesc(Query *parsetree,
|
||||
qd->operation = parsetree->commandType; /* operation */
|
||||
qd->parsetree = parsetree; /* parse tree */
|
||||
qd->plantree = plantree; /* plan */
|
||||
qd->snapshot = snapshot; /* snapshot */
|
||||
qd->crosscheck_snapshot = crosscheck_snapshot; /* RI check snapshot */
|
||||
qd->dest = dest; /* output dest */
|
||||
qd->params = params; /* parameter values passed into query */
|
||||
qd->doInstrument = doInstrument; /* instrumentation wanted? */
|
||||
@ -90,7 +99,7 @@ FreeQueryDesc(QueryDesc *qdesc)
|
||||
|
||||
/*
|
||||
* ProcessQuery
|
||||
* Execute a single query
|
||||
* Execute a single plannable query within a PORTAL_MULTI_QUERY portal
|
||||
*
|
||||
* parsetree: the query tree
|
||||
* plan: the plan tree for the query
|
||||
@ -104,7 +113,7 @@ FreeQueryDesc(QueryDesc *qdesc)
|
||||
* Must be called in a memory context that will be reset or deleted on
|
||||
* error; otherwise the executor's memory usage will be leaked.
|
||||
*/
|
||||
void
|
||||
static void
|
||||
ProcessQuery(Query *parsetree,
|
||||
Plan *plan,
|
||||
ParamListInfo params,
|
||||
@ -114,6 +123,9 @@ ProcessQuery(Query *parsetree,
|
||||
int operation = parsetree->commandType;
|
||||
QueryDesc *queryDesc;
|
||||
|
||||
ereport(DEBUG3,
|
||||
(errmsg_internal("ProcessQuery")));
|
||||
|
||||
/*
|
||||
* Check for special-case destinations
|
||||
*/
|
||||
@ -132,10 +144,18 @@ ProcessQuery(Query *parsetree,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Must always set snapshot for plannable queries. Note we assume
|
||||
* that caller will take care of restoring ActiveSnapshot on exit/error.
|
||||
*/
|
||||
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
|
||||
|
||||
/*
|
||||
* Create the QueryDesc object
|
||||
*/
|
||||
queryDesc = CreateQueryDesc(parsetree, plan, dest, params, false);
|
||||
queryDesc = CreateQueryDesc(parsetree, plan,
|
||||
ActiveSnapshot, InvalidSnapshot,
|
||||
dest, params, false);
|
||||
|
||||
/*
|
||||
* Set up to collect AFTER triggers
|
||||
@ -145,7 +165,7 @@ ProcessQuery(Query *parsetree,
|
||||
/*
|
||||
* Call ExecStart to prepare the plan for execution
|
||||
*/
|
||||
ExecutorStart(queryDesc, false, false);
|
||||
ExecutorStart(queryDesc, false);
|
||||
|
||||
/*
|
||||
* Run the plan to completion.
|
||||
@ -195,6 +215,9 @@ ProcessQuery(Query *parsetree,
|
||||
AfterTriggerEndQuery();
|
||||
|
||||
FreeQueryDesc(queryDesc);
|
||||
|
||||
FreeSnapshot(ActiveSnapshot);
|
||||
ActiveSnapshot = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -238,13 +261,19 @@ ChoosePortalStrategy(List *parseTrees)
|
||||
* the query, they must be passed in here (caller is responsible for
|
||||
* giving them appropriate lifetime).
|
||||
*
|
||||
* The caller can optionally pass a snapshot to be used; pass InvalidSnapshot
|
||||
* for the normal behavior of setting a new snapshot. This parameter is
|
||||
* presently ignored for non-PORTAL_ONE_SELECT portals (it's only intended
|
||||
* to be used for cursors).
|
||||
*
|
||||
* On return, portal is ready to accept PortalRun() calls, and the result
|
||||
* tupdesc (if any) is known.
|
||||
*/
|
||||
void
|
||||
PortalStart(Portal portal, ParamListInfo params)
|
||||
PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
|
||||
{
|
||||
Portal saveActivePortal;
|
||||
Snapshot saveActiveSnapshot;
|
||||
ResourceOwner saveResourceOwner;
|
||||
MemoryContext savePortalContext;
|
||||
MemoryContext oldContext;
|
||||
@ -259,11 +288,13 @@ PortalStart(Portal portal, ParamListInfo params)
|
||||
* QueryContext?)
|
||||
*/
|
||||
saveActivePortal = ActivePortal;
|
||||
saveActiveSnapshot = ActiveSnapshot;
|
||||
saveResourceOwner = CurrentResourceOwner;
|
||||
savePortalContext = PortalContext;
|
||||
PG_TRY();
|
||||
{
|
||||
ActivePortal = portal;
|
||||
ActiveSnapshot = NULL; /* will be set later */
|
||||
CurrentResourceOwner = portal->resowner;
|
||||
PortalContext = PortalGetHeapMemory(portal);
|
||||
|
||||
@ -285,9 +316,13 @@ PortalStart(Portal portal, ParamListInfo params)
|
||||
case PORTAL_ONE_SELECT:
|
||||
|
||||
/*
|
||||
* Must set query snapshot before starting executor.
|
||||
* Must set snapshot before starting executor. Be sure to
|
||||
* copy it into the portal's context.
|
||||
*/
|
||||
SetQuerySnapshot();
|
||||
if (snapshot)
|
||||
ActiveSnapshot = CopySnapshot(snapshot);
|
||||
else
|
||||
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
|
||||
|
||||
/*
|
||||
* Create QueryDesc in portal's context; for the moment,
|
||||
@ -295,6 +330,8 @@ PortalStart(Portal portal, ParamListInfo params)
|
||||
*/
|
||||
queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees),
|
||||
(Plan *) linitial(portal->planTrees),
|
||||
ActiveSnapshot,
|
||||
InvalidSnapshot,
|
||||
None_Receiver,
|
||||
params,
|
||||
false);
|
||||
@ -309,7 +346,7 @@ PortalStart(Portal portal, ParamListInfo params)
|
||||
/*
|
||||
* Call ExecStart to prepare the plan for execution
|
||||
*/
|
||||
ExecutorStart(queryDesc, false, false);
|
||||
ExecutorStart(queryDesc, false);
|
||||
|
||||
/*
|
||||
* This tells PortalCleanup to shut down the executor
|
||||
@ -333,8 +370,8 @@ PortalStart(Portal portal, ParamListInfo params)
|
||||
case PORTAL_UTIL_SELECT:
|
||||
|
||||
/*
|
||||
* We don't set query snapshot here, because
|
||||
* PortalRunUtility will take care of it.
|
||||
* We don't set snapshot here, because
|
||||
* PortalRunUtility will take care of it if needed.
|
||||
*/
|
||||
portal->tupDesc =
|
||||
UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt);
|
||||
@ -361,6 +398,7 @@ PortalStart(Portal portal, ParamListInfo params)
|
||||
|
||||
/* Restore global vars and propagate error */
|
||||
ActivePortal = saveActivePortal;
|
||||
ActiveSnapshot = saveActiveSnapshot;
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
PortalContext = savePortalContext;
|
||||
|
||||
@ -371,6 +409,7 @@ PortalStart(Portal portal, ParamListInfo params)
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
ActivePortal = saveActivePortal;
|
||||
ActiveSnapshot = saveActiveSnapshot;
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
PortalContext = savePortalContext;
|
||||
|
||||
@ -453,6 +492,7 @@ PortalRun(Portal portal, long count,
|
||||
{
|
||||
bool result;
|
||||
Portal saveActivePortal;
|
||||
Snapshot saveActiveSnapshot;
|
||||
ResourceOwner saveResourceOwner;
|
||||
MemoryContext savePortalContext;
|
||||
MemoryContext saveQueryContext;
|
||||
@ -485,12 +525,14 @@ PortalRun(Portal portal, long count,
|
||||
* Set up global portal context pointers.
|
||||
*/
|
||||
saveActivePortal = ActivePortal;
|
||||
saveActiveSnapshot = ActiveSnapshot;
|
||||
saveResourceOwner = CurrentResourceOwner;
|
||||
savePortalContext = PortalContext;
|
||||
saveQueryContext = QueryContext;
|
||||
PG_TRY();
|
||||
{
|
||||
ActivePortal = portal;
|
||||
ActiveSnapshot = NULL; /* will be set later */
|
||||
CurrentResourceOwner = portal->resowner;
|
||||
PortalContext = PortalGetHeapMemory(portal);
|
||||
QueryContext = portal->queryContext;
|
||||
@ -579,6 +621,7 @@ PortalRun(Portal portal, long count,
|
||||
|
||||
/* Restore global vars and propagate error */
|
||||
ActivePortal = saveActivePortal;
|
||||
ActiveSnapshot = saveActiveSnapshot;
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
PortalContext = savePortalContext;
|
||||
QueryContext = saveQueryContext;
|
||||
@ -590,6 +633,7 @@ PortalRun(Portal portal, long count,
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
ActivePortal = saveActivePortal;
|
||||
ActiveSnapshot = saveActiveSnapshot;
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
PortalContext = savePortalContext;
|
||||
QueryContext = saveQueryContext;
|
||||
@ -670,6 +714,7 @@ PortalRunSelect(Portal portal,
|
||||
nprocessed = RunFromStore(portal, direction, count, dest);
|
||||
else
|
||||
{
|
||||
ActiveSnapshot = queryDesc->snapshot;
|
||||
ExecutorRun(queryDesc, direction, count);
|
||||
nprocessed = queryDesc->estate->es_processed;
|
||||
}
|
||||
@ -711,6 +756,7 @@ PortalRunSelect(Portal portal,
|
||||
nprocessed = RunFromStore(portal, direction, count, dest);
|
||||
else
|
||||
{
|
||||
ActiveSnapshot = queryDesc->snapshot;
|
||||
ExecutorRun(queryDesc, direction, count);
|
||||
nprocessed = queryDesc->estate->es_processed;
|
||||
}
|
||||
@ -834,6 +880,9 @@ PortalRunUtility(Portal portal, Query *query,
|
||||
* the database --- if, say, it has to update an index with
|
||||
* expressions that invoke user-defined functions, then it had better
|
||||
* have a snapshot.
|
||||
*
|
||||
* Note we assume that caller will take care of restoring ActiveSnapshot
|
||||
* on exit/error.
|
||||
*/
|
||||
if (!(IsA(utilityStmt, TransactionStmt) ||
|
||||
IsA(utilityStmt, LockStmt) ||
|
||||
@ -847,7 +896,9 @@ PortalRunUtility(Portal portal, Query *query,
|
||||
IsA(utilityStmt, NotifyStmt) ||
|
||||
IsA(utilityStmt, UnlistenStmt) ||
|
||||
IsA(utilityStmt, CheckPointStmt)))
|
||||
SetQuerySnapshot();
|
||||
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
|
||||
else
|
||||
ActiveSnapshot = NULL;
|
||||
|
||||
if (query->canSetTag)
|
||||
{
|
||||
@ -864,6 +915,10 @@ PortalRunUtility(Portal portal, Query *query,
|
||||
|
||||
/* Some utility statements may change context on us */
|
||||
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||
|
||||
if (ActiveSnapshot)
|
||||
FreeSnapshot(ActiveSnapshot);
|
||||
ActiveSnapshot = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -924,15 +979,6 @@ PortalRunMulti(Portal portal,
|
||||
/*
|
||||
* process a plannable query.
|
||||
*/
|
||||
ereport(DEBUG3,
|
||||
(errmsg_internal("ProcessQuery")));
|
||||
|
||||
/* Must always set snapshot for plannable queries */
|
||||
SetQuerySnapshot();
|
||||
|
||||
/*
|
||||
* execute the plan
|
||||
*/
|
||||
if (log_executor_stats)
|
||||
ResetUsage();
|
||||
|
||||
@ -1005,6 +1051,7 @@ PortalRunFetch(Portal portal,
|
||||
{
|
||||
long result;
|
||||
Portal saveActivePortal;
|
||||
Snapshot saveActiveSnapshot;
|
||||
ResourceOwner saveResourceOwner;
|
||||
MemoryContext savePortalContext;
|
||||
MemoryContext saveQueryContext;
|
||||
@ -1025,12 +1072,14 @@ PortalRunFetch(Portal portal,
|
||||
* Set up global portal context pointers.
|
||||
*/
|
||||
saveActivePortal = ActivePortal;
|
||||
saveActiveSnapshot = ActiveSnapshot;
|
||||
saveResourceOwner = CurrentResourceOwner;
|
||||
savePortalContext = PortalContext;
|
||||
saveQueryContext = QueryContext;
|
||||
PG_TRY();
|
||||
{
|
||||
ActivePortal = portal;
|
||||
ActiveSnapshot = NULL; /* will be set later */
|
||||
CurrentResourceOwner = portal->resowner;
|
||||
PortalContext = PortalGetHeapMemory(portal);
|
||||
QueryContext = portal->queryContext;
|
||||
@ -1056,6 +1105,7 @@ PortalRunFetch(Portal portal,
|
||||
|
||||
/* Restore global vars and propagate error */
|
||||
ActivePortal = saveActivePortal;
|
||||
ActiveSnapshot = saveActiveSnapshot;
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
PortalContext = savePortalContext;
|
||||
QueryContext = saveQueryContext;
|
||||
@ -1070,6 +1120,7 @@ PortalRunFetch(Portal portal,
|
||||
portal->status = PORTAL_READY;
|
||||
|
||||
ActivePortal = saveActivePortal;
|
||||
ActiveSnapshot = saveActiveSnapshot;
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
PortalContext = savePortalContext;
|
||||
QueryContext = saveQueryContext;
|
||||
|
Reference in New Issue
Block a user