Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 778a21c

Browse files
committed
Tweak portal (cursor) code so that it will not call the executor again
when user does another FETCH after reaching end of data, or another FETCH backwards after reaching start. This is needed because some plan nodes are not very robust about being called again after they've already returned NULL; for example, MergeJoin will crash in some states but not others. While the ideal approach would be for them all to handle this correctly, it seems foolish to assume that no such bugs would creep in again once cleaned up. Therefore, the most robust answer is to prevent the situation from arising at all.
1 parent f5ea88a commit 778a21c

File tree

4 files changed

+77
-108
lines changed

4 files changed

+77
-108
lines changed

src/backend/commands/command.c

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.121 2001/02/14 21:35:00 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.122 2001/02/27 22:07:34 tgl Exp $
1212
*
1313
* NOTES
1414
* The PerformAddAttribute() code, like most of the relation
@@ -107,8 +107,8 @@ PerformPortalFetch(char *name,
107107
CommandDest dest)
108108
{
109109
Portal portal;
110-
int feature;
111110
QueryDesc *queryDesc;
111+
EState *estate;
112112
MemoryContext oldcontext;
113113

114114
/* ----------------
@@ -140,19 +140,14 @@ PerformPortalFetch(char *name,
140140
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
141141

142142
/* ----------------
143-
* setup "feature" to tell the executor which direction to go in.
144-
* ----------------
145-
*/
146-
if (forward)
147-
feature = EXEC_FOR;
148-
else
149-
feature = EXEC_BACK;
150-
151-
/* ----------------
152-
* tell the destination to prepare to receive some tuples
143+
* tell the destination to prepare to receive some tuples.
144+
*
145+
* If we've been asked for a MOVE, make a temporary QueryDesc
146+
* with the appropriate dummy destination.
153147
* ----------------
154148
*/
155149
queryDesc = PortalGetQueryDesc(portal);
150+
estate = PortalGetState(portal);
156151

157152
if (dest == None) /* MOVE */
158153
{
@@ -165,7 +160,7 @@ PerformPortalFetch(char *name,
165160

166161
BeginCommand(name,
167162
queryDesc->operation,
168-
portal->attinfo, /* QueryDescGetTypeInfo(queryDesc) */
163+
PortalGetTupleDesc(portal),
169164
false, /* portal fetches don't end up in
170165
* relations */
171166
false, /* this is a portal fetch, not a "retrieve
@@ -174,18 +169,45 @@ PerformPortalFetch(char *name,
174169
dest);
175170

176171
/* ----------------
177-
* execute the portal fetch operation
172+
* Determine which direction to go in, and check to see if we're already
173+
* at the end of the available tuples in that direction. If so, do
174+
* nothing. (This check exists because not all plan node types are
175+
* robust about being called again if they've already returned NULL
176+
* once.) If it's OK to do the fetch, call the executor. Then,
177+
* update the atStart/atEnd state depending on the number of tuples
178+
* that were retrieved.
178179
* ----------------
179180
*/
180-
ExecutorRun(queryDesc, PortalGetState(portal), feature, (long) count);
181-
182-
if (dest == None) /* MOVE */
183-
pfree(queryDesc);
181+
if (forward)
182+
{
183+
if (! portal->atEnd)
184+
{
185+
ExecutorRun(queryDesc, estate, EXEC_FOR, (long) count);
186+
if (estate->es_processed > 0)
187+
portal->atStart = false; /* OK to back up now */
188+
if (count <= 0 || (int) estate->es_processed < count)
189+
portal->atEnd = true; /* we retrieved 'em all */
190+
}
191+
}
192+
else
193+
{
194+
if (! portal->atStart)
195+
{
196+
ExecutorRun(queryDesc, estate, EXEC_BACK, (long) count);
197+
if (estate->es_processed > 0)
198+
portal->atEnd = false; /* OK to go forward now */
199+
if (count <= 0 || (int) estate->es_processed < count)
200+
portal->atStart = true; /* we retrieved 'em all */
201+
}
202+
}
184203

185204
/* ----------------
186-
* Switch back to old context.
205+
* Clean up and switch back to old context.
187206
* ----------------
188207
*/
208+
if (dest == None) /* MOVE */
209+
pfree(queryDesc);
210+
189211
MemoryContextSwitchTo(oldcontext);
190212

191213
/* ----------------

src/backend/tcop/pquery.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.41 2001/01/24 19:43:09 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.42 2001/02/27 22:07:34 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -248,7 +248,7 @@ ProcessQuery(Query *parsetree,
248248
* ----------------
249249
*/
250250
if (isRetrieveIntoRelation)
251-
queryDesc->dest = (int) None;
251+
queryDesc->dest = None;
252252

253253
/* ----------------
254254
* create a default executor state.

src/backend/utils/mmgr/portalmem.c

Lines changed: 11 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -8,44 +8,26 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.39 2001/01/24 19:43:17 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.40 2001/02/27 22:07:34 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
1515
/*
1616
* NOTES
17-
* Do not confuse "Portal" with "PortalEntry" (or "PortalBuffer").
18-
* When a PQexec() routine is run, the resulting tuples
19-
* find their way into a "PortalEntry". The contents of the resulting
20-
* "PortalEntry" can then be inspected by other PQxxx functions.
17+
* A "Portal" is a structure used to keep track of cursor queries.
2118
*
22-
* A "Portal" is a structure used to keep track of queries of the
23-
* form:
24-
* retrieve portal FOO ( blah... ) where blah...
25-
*
26-
* When the backend sees a "retrieve portal" query, it allocates
27-
* a "PortalD" structure, plans the query and then stores the query
19+
* When the backend sees a "declare cursor" query, it allocates a
20+
* "PortalData" structure, plans the query and then stores the query
2821
* in the portal without executing it. Later, when the backend
2922
* sees a
30-
* fetch 1 into FOO
31-
*
23+
* fetch 1 from FOO
3224
* the system looks up the portal named "FOO" in the portal table,
3325
* gets the planned query and then calls the executor with a feature of
3426
* '(EXEC_FOR 1). The executor then runs the query and returns a single
3527
* tuple. The problem is that we have to hold onto the state of the
36-
* portal query until we see a "close p". This means we have to be
28+
* portal query until we see a "close". This means we have to be
3729
* careful about memory management.
3830
*
39-
* Having said all that, here is what a PortalD currently looks like:
40-
*
41-
* struct PortalD {
42-
* char* name;
43-
* classObj(MemoryContext) heap;
44-
* List queryDesc;
45-
* EState state;
46-
* void (*cleanup) ARGS((Portal portal));
47-
* };
48-
*
4931
* I hope this makes things clearer to whoever reads this -cim 2/22/91
5032
*/
5133

@@ -188,43 +170,13 @@ PortalSetQuery(Portal portal,
188170
AssertArg(IsA((Node *) state, EState));
189171

190172
portal->queryDesc = queryDesc;
191-
portal->state = state;
192173
portal->attinfo = attinfo;
174+
portal->state = state;
175+
portal->atStart = true; /* Allow fetch forward only */
176+
portal->atEnd = false;
193177
portal->cleanup = cleanup;
194178
}
195179

196-
/*
197-
* PortalGetQueryDesc
198-
* Returns query attached to portal.
199-
*
200-
* Exceptions:
201-
* BadState if called when disabled.
202-
* BadArg if portal is invalid.
203-
*/
204-
QueryDesc *
205-
PortalGetQueryDesc(Portal portal)
206-
{
207-
AssertArg(PortalIsValid(portal));
208-
209-
return portal->queryDesc;
210-
}
211-
212-
/*
213-
* PortalGetState
214-
* Returns state attached to portal.
215-
*
216-
* Exceptions:
217-
* BadState if called when disabled.
218-
* BadArg if portal is invalid.
219-
*/
220-
EState *
221-
PortalGetState(Portal portal)
222-
{
223-
AssertArg(PortalIsValid(portal));
224-
225-
return portal->state;
226-
}
227-
228180
/*
229181
* CreatePortal
230182
* Returns a new portal given a name.
@@ -265,7 +217,8 @@ CreatePortal(char *name)
265217
portal->queryDesc = NULL;
266218
portal->attinfo = NULL;
267219
portal->state = NULL;
268-
220+
portal->atStart = true; /* disallow fetches until query is set */
221+
portal->atEnd = true;
269222
portal->cleanup = NULL;
270223

271224
/* put portal in table */
@@ -315,13 +268,3 @@ AtEOXact_portals(void)
315268
{
316269
HashTableWalk(PortalHashTable, (HashtFunc) PortalDrop, 0);
317270
}
318-
319-
/*
320-
* PortalGetHeapMemory
321-
* Returns heap memory context for a given portal.
322-
*/
323-
MemoryContext
324-
PortalGetHeapMemory(Portal portal)
325-
{
326-
return portal->heap;
327-
}

src/include/utils/portal.h

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,14 @@
77
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $Id: portal.h,v 1.25 2001/01/24 19:43:28 momjian Exp $
10+
* $Id: portal.h,v 1.26 2001/02/27 22:07:34 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
1414
/*
1515
* Note:
1616
* A portal is an abstraction which represents the execution state of
17-
* a running query (or a fixed sequence of queries).
18-
*
19-
* Note:
20-
* now that PQ calls can be made from within a backend, a portal
21-
* may also be used to keep track of the tuples resulting
22-
* from the execution of a query. In this case, entryIndex
17+
* a running query (specifically, a CURSOR).
2318
*/
2419
#ifndef PORTAL_H
2520
#define PORTAL_H
@@ -28,24 +23,41 @@
2823
#include "nodes/memnodes.h"
2924

3025

31-
typedef struct PortalD *Portal;
26+
typedef struct PortalData *Portal;
3227

33-
typedef struct PortalD
28+
typedef struct PortalData
3429
{
3530
char *name; /* Portal's name */
3631
MemoryContext heap; /* subsidiary memory */
3732
QueryDesc *queryDesc; /* Info about query associated with portal */
3833
TupleDesc attinfo;
39-
EState *state;
34+
EState *state; /* Execution state of query */
35+
bool atStart; /* T => fetch backwards is not allowed */
36+
bool atEnd; /* T => fetch forwards is not allowed */
4037
void (*cleanup) (Portal); /* Cleanup routine (optional) */
41-
} PortalD;
38+
} PortalData;
4239

4340
/*
4441
* PortalIsValid
4542
* True iff portal is valid.
4643
*/
4744
#define PortalIsValid(p) PointerIsValid(p)
4845

46+
/*
47+
* Access macros for Portal ... use these in preference to field access.
48+
*/
49+
#define PortalGetQueryDesc(portal) ((portal)->queryDesc)
50+
#define PortalGetTupleDesc(portal) ((portal)->attinfo)
51+
#define PortalGetState(portal) ((portal)->state)
52+
#define PortalGetHeapMemory(portal) ((portal)->heap)
53+
54+
/*
55+
* estimate of the maximum number of open portals a user would have,
56+
* used in initially sizing the PortalHashTable in EnablePortalManager()
57+
*/
58+
#define PORTALS_PER_USER 10
59+
60+
4961
extern void EnablePortalManager(void);
5062
extern void AtEOXact_portals(void);
5163
extern Portal CreatePortal(char *name);
@@ -54,14 +66,6 @@ extern Portal GetPortalByName(char *name);
5466
extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc,
5567
TupleDesc attinfo, EState *state,
5668
void (*cleanup) (Portal portal));
57-
extern QueryDesc *PortalGetQueryDesc(Portal portal);
58-
extern EState *PortalGetState(Portal portal);
59-
extern MemoryContext PortalGetHeapMemory(Portal portal);
60-
61-
/* estimate of the maximum number of open portals a user would have,
62-
* used in initially sizing the PortalHashTable in EnablePortalManager()
63-
*/
64-
#define PORTALS_PER_USER 10
6569

6670

6771
#endif /* PORTAL_H */

0 commit comments

Comments
 (0)