2002-04-15 05:22:04 +00:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* portalcmds.c
|
|
|
|
* portal support code
|
|
|
|
*
|
|
|
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2003-03-11 19:40:24 +00:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.10 2003/03/11 19:40:22 tgl Exp $
|
2002-04-15 05:22:04 +00:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2002-11-13 00:44:09 +00:00
|
|
|
#include <limits.h>
|
|
|
|
|
2002-04-15 05:22:04 +00:00
|
|
|
#include "commands/portalcmds.h"
|
|
|
|
#include "executor/executor.h"
|
2003-03-10 03:53:52 +00:00
|
|
|
#include "optimizer/planner.h"
|
|
|
|
#include "rewrite/rewriteHandler.h"
|
|
|
|
|
|
|
|
|
2003-03-11 19:40:24 +00:00
|
|
|
static long DoRelativeFetch(Portal portal,
|
|
|
|
bool forward,
|
|
|
|
long count,
|
|
|
|
CommandDest dest);
|
|
|
|
static void DoPortalRewind(Portal portal);
|
2003-03-10 03:53:52 +00:00
|
|
|
static Portal PreparePortal(char *portalName);
|
2002-04-15 05:22:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
2003-03-10 03:53:52 +00:00
|
|
|
* PerformCursorOpen
|
|
|
|
* Execute SQL DECLARE CURSOR command.
|
2002-04-15 05:22:04 +00:00
|
|
|
*/
|
|
|
|
void
|
2003-03-10 03:53:52 +00:00
|
|
|
PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
|
2002-04-15 05:22:04 +00:00
|
|
|
{
|
2003-03-10 03:53:52 +00:00
|
|
|
List *rewritten;
|
|
|
|
Query *query;
|
|
|
|
Plan *plan;
|
|
|
|
Portal portal;
|
|
|
|
MemoryContext oldContext;
|
|
|
|
char *cursorName;
|
|
|
|
QueryDesc *queryDesc;
|
|
|
|
|
|
|
|
/* Check for invalid context (must be in transaction block) */
|
|
|
|
RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
|
|
|
|
|
2002-04-15 05:22:04 +00:00
|
|
|
/*
|
2003-03-10 03:53:52 +00:00
|
|
|
* The query has been through parse analysis, but not rewriting or
|
|
|
|
* planning as yet. Note that the grammar ensured we have a SELECT
|
|
|
|
* query, so we are not expecting rule rewriting to do anything strange.
|
2002-04-15 05:22:04 +00:00
|
|
|
*/
|
2003-03-10 03:53:52 +00:00
|
|
|
rewritten = QueryRewrite((Query *) stmt->query);
|
|
|
|
if (length(rewritten) != 1 || !IsA(lfirst(rewritten), Query))
|
|
|
|
elog(ERROR, "PerformCursorOpen: unexpected rewrite result");
|
|
|
|
query = (Query *) lfirst(rewritten);
|
|
|
|
if (query->commandType != CMD_SELECT)
|
|
|
|
elog(ERROR, "PerformCursorOpen: unexpected rewrite result");
|
|
|
|
|
|
|
|
if (query->into)
|
|
|
|
elog(ERROR, "DECLARE CURSOR may not specify INTO");
|
|
|
|
if (query->rowMarks != NIL)
|
|
|
|
elog(ERROR, "DECLARE/UPDATE is not supported"
|
|
|
|
"\n\tCursors must be READ ONLY");
|
|
|
|
|
|
|
|
plan = planner(query, true, stmt->options);
|
|
|
|
|
|
|
|
/* If binary cursor, switch to alternate output format */
|
|
|
|
if ((stmt->options & CURSOR_OPT_BINARY) && dest == Remote)
|
|
|
|
dest = RemoteInternal;
|
2002-04-15 05:22:04 +00:00
|
|
|
|
|
|
|
/*
|
2003-03-10 03:53:52 +00:00
|
|
|
* Create a portal and copy the query and plan into its memory context.
|
2002-04-15 05:22:04 +00:00
|
|
|
*/
|
2003-03-10 03:53:52 +00:00
|
|
|
portal = PreparePortal(stmt->portalname);
|
|
|
|
|
|
|
|
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
|
|
|
query = copyObject(query);
|
|
|
|
plan = copyObject(plan);
|
2002-04-15 05:22:04 +00:00
|
|
|
|
|
|
|
/*
|
2003-03-10 03:53:52 +00:00
|
|
|
* Create the QueryDesc object in the portal context, too.
|
2002-04-15 05:22:04 +00:00
|
|
|
*/
|
2003-03-10 03:53:52 +00:00
|
|
|
cursorName = pstrdup(stmt->portalname);
|
|
|
|
queryDesc = CreateQueryDesc(query, plan, dest, cursorName, NULL, false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call ExecStart to prepare the plan for execution
|
|
|
|
*/
|
|
|
|
ExecutorStart(queryDesc);
|
2002-04-15 05:22:04 +00:00
|
|
|
|
2003-03-10 03:53:52 +00:00
|
|
|
/* Arrange to shut down the executor if portal is dropped */
|
|
|
|
PortalSetQuery(portal, queryDesc, PortalCleanup);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We're done; the query won't actually be run until PerformPortalFetch
|
|
|
|
* is called.
|
|
|
|
*/
|
|
|
|
MemoryContextSwitchTo(oldContext);
|
|
|
|
}
|
2002-04-15 05:22:04 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* PerformPortalFetch
|
2003-03-10 03:53:52 +00:00
|
|
|
* Execute SQL FETCH or MOVE command.
|
2002-04-15 05:22:04 +00:00
|
|
|
*
|
2003-03-11 19:40:24 +00:00
|
|
|
* stmt: parsetree node for command
|
2002-04-15 05:22:04 +00:00
|
|
|
* dest: where to send results
|
|
|
|
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
|
|
|
|
* in which to store a command completion status string.
|
|
|
|
*
|
|
|
|
* completionTag may be NULL if caller doesn't want a status string.
|
|
|
|
*/
|
|
|
|
void
|
2003-03-11 19:40:24 +00:00
|
|
|
PerformPortalFetch(FetchStmt *stmt,
|
2002-04-15 05:22:04 +00:00
|
|
|
CommandDest dest,
|
|
|
|
char *completionTag)
|
|
|
|
{
|
|
|
|
Portal portal;
|
2003-03-10 03:53:52 +00:00
|
|
|
long nprocessed;
|
2002-04-15 05:22:04 +00:00
|
|
|
|
|
|
|
/* initialize completion status in case of early exit */
|
|
|
|
if (completionTag)
|
2003-03-11 19:40:24 +00:00
|
|
|
strcpy(completionTag, stmt->ismove ? "MOVE 0" : "FETCH 0");
|
2002-04-15 05:22:04 +00:00
|
|
|
|
2003-03-10 03:53:52 +00:00
|
|
|
/* get the portal from the portal name */
|
2003-03-11 19:40:24 +00:00
|
|
|
portal = GetPortalByName(stmt->portalname);
|
2002-04-15 05:22:04 +00:00
|
|
|
if (!PortalIsValid(portal))
|
|
|
|
{
|
|
|
|
elog(WARNING, "PerformPortalFetch: portal \"%s\" not found",
|
2003-03-11 19:40:24 +00:00
|
|
|
stmt->portalname);
|
2002-04-15 05:22:04 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-03-10 03:53:52 +00:00
|
|
|
/* Do it */
|
2003-03-11 19:40:24 +00:00
|
|
|
nprocessed = DoPortalFetch(portal,
|
|
|
|
stmt->direction,
|
|
|
|
stmt->howMany,
|
|
|
|
stmt->ismove ? None : dest);
|
2003-03-10 03:53:52 +00:00
|
|
|
|
|
|
|
/* Return command status if wanted */
|
|
|
|
if (completionTag)
|
|
|
|
snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
|
2003-03-11 19:40:24 +00:00
|
|
|
stmt->ismove ? "MOVE" : "FETCH",
|
2003-03-10 03:53:52 +00:00
|
|
|
nprocessed);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DoPortalFetch
|
2003-03-11 19:40:24 +00:00
|
|
|
* Guts of PerformPortalFetch --- shared with SPI cursor operations.
|
|
|
|
* Caller must already have validated the Portal.
|
2003-03-10 03:53:52 +00:00
|
|
|
*
|
2003-03-11 19:40:24 +00:00
|
|
|
* Returns number of rows processed (suitable for use in result tag)
|
2003-03-10 03:53:52 +00:00
|
|
|
*/
|
|
|
|
long
|
2003-03-11 19:40:24 +00:00
|
|
|
DoPortalFetch(Portal portal,
|
|
|
|
FetchDirection fdirection,
|
|
|
|
long count,
|
|
|
|
CommandDest dest)
|
2003-03-10 03:53:52 +00:00
|
|
|
{
|
2003-03-11 19:40:24 +00:00
|
|
|
bool forward;
|
|
|
|
|
|
|
|
switch (fdirection)
|
|
|
|
{
|
|
|
|
case FETCH_FORWARD:
|
|
|
|
if (count < 0)
|
|
|
|
{
|
|
|
|
fdirection = FETCH_BACKWARD;
|
|
|
|
count = -count;
|
|
|
|
}
|
|
|
|
/* fall out of switch to share code with FETCH_BACKWARD */
|
|
|
|
break;
|
|
|
|
case FETCH_BACKWARD:
|
|
|
|
if (count < 0)
|
|
|
|
{
|
|
|
|
fdirection = FETCH_FORWARD;
|
|
|
|
count = -count;
|
|
|
|
}
|
|
|
|
/* fall out of switch to share code with FETCH_FORWARD */
|
|
|
|
break;
|
|
|
|
case FETCH_ABSOLUTE:
|
|
|
|
if (count > 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Definition: Rewind to start, advance count-1 rows, return
|
|
|
|
* next row (if any). In practice, if the goal is less than
|
|
|
|
* halfway back to the start, it's better to scan from where
|
|
|
|
* we are. In any case, we arrange to fetch the target row
|
|
|
|
* going forwards.
|
|
|
|
*/
|
|
|
|
if (portal->posOverflow || portal->portalPos == LONG_MAX ||
|
|
|
|
count-1 <= portal->portalPos / 2)
|
|
|
|
{
|
|
|
|
DoPortalRewind(portal);
|
|
|
|
if (count > 1)
|
|
|
|
DoRelativeFetch(portal, true, count-1, None);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
long pos = portal->portalPos;
|
|
|
|
|
|
|
|
if (portal->atEnd)
|
|
|
|
pos++; /* need one extra fetch if off end */
|
|
|
|
if (count <= pos)
|
|
|
|
DoRelativeFetch(portal, false, pos-count+1, None);
|
|
|
|
else if (count > pos+1)
|
|
|
|
DoRelativeFetch(portal, true, count-pos-1, None);
|
|
|
|
}
|
|
|
|
return DoRelativeFetch(portal, true, 1L, dest);
|
|
|
|
}
|
|
|
|
else if (count < 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Definition: Advance to end, back up abs(count)-1 rows,
|
|
|
|
* return prior row (if any). We could optimize this if we
|
|
|
|
* knew in advance where the end was, but typically we won't.
|
|
|
|
* (Is it worth considering case where count > half of size
|
|
|
|
* of query? We could rewind once we know the size ...)
|
|
|
|
*/
|
|
|
|
DoRelativeFetch(portal, true, FETCH_ALL, None);
|
|
|
|
if (count < -1)
|
|
|
|
DoRelativeFetch(portal, false, -count-1, None);
|
|
|
|
return DoRelativeFetch(portal, false, 1L, dest);
|
|
|
|
}
|
|
|
|
else /* count == 0 */
|
|
|
|
{
|
|
|
|
/* Rewind to start, return zero rows */
|
|
|
|
DoPortalRewind(portal);
|
|
|
|
return DoRelativeFetch(portal, true, 0L, dest);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FETCH_RELATIVE:
|
|
|
|
if (count > 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Definition: advance count-1 rows, return next row (if any).
|
|
|
|
*/
|
|
|
|
if (count > 1)
|
|
|
|
DoRelativeFetch(portal, true, count-1, None);
|
|
|
|
return DoRelativeFetch(portal, true, 1L, dest);
|
|
|
|
}
|
|
|
|
else if (count < 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Definition: back up abs(count)-1 rows, return prior row
|
|
|
|
* (if any).
|
|
|
|
*/
|
|
|
|
if (count < -1)
|
|
|
|
DoRelativeFetch(portal, false, -count-1, None);
|
|
|
|
return DoRelativeFetch(portal, false, 1L, dest);
|
|
|
|
}
|
|
|
|
else /* count == 0 */
|
|
|
|
{
|
|
|
|
/* Same as FETCH FORWARD 0, so fall out of switch */
|
|
|
|
fdirection = FETCH_FORWARD;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(ERROR, "DoPortalFetch: bogus direction");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get here with fdirection == FETCH_FORWARD or FETCH_BACKWARD,
|
|
|
|
* and count >= 0.
|
|
|
|
*/
|
|
|
|
forward = (fdirection == FETCH_FORWARD);
|
2003-03-10 03:53:52 +00:00
|
|
|
|
2003-01-08 00:22:27 +00:00
|
|
|
/*
|
|
|
|
* Zero count means to re-fetch the current row, if any (per SQL92)
|
|
|
|
*/
|
2002-11-13 00:44:09 +00:00
|
|
|
if (count == 0)
|
2002-12-30 15:31:51 +00:00
|
|
|
{
|
2003-01-08 00:22:27 +00:00
|
|
|
bool on_row;
|
2002-12-30 15:31:51 +00:00
|
|
|
|
|
|
|
/* Are we sitting on a row? */
|
2003-03-11 19:40:24 +00:00
|
|
|
on_row = (!portal->atStart && !portal->atEnd);
|
2002-12-30 15:31:51 +00:00
|
|
|
|
|
|
|
if (dest == None)
|
|
|
|
{
|
|
|
|
/* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */
|
2003-03-10 03:53:52 +00:00
|
|
|
return on_row ? 1L : 0L;
|
2002-12-30 15:31:51 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-01-08 00:22:27 +00:00
|
|
|
/*
|
|
|
|
* If we are sitting on a row, back up one so we can re-fetch it.
|
|
|
|
* If we are not sitting on a row, we still have to start up and
|
|
|
|
* shut down the executor so that the destination is initialized
|
2003-03-11 19:40:24 +00:00
|
|
|
* and shut down correctly; so keep going. To DoRelativeFetch,
|
|
|
|
* count == 0 means we will retrieve no row.
|
2003-01-08 00:22:27 +00:00
|
|
|
*/
|
|
|
|
if (on_row)
|
|
|
|
{
|
2003-03-11 19:40:24 +00:00
|
|
|
DoRelativeFetch(portal, false, 1L, None);
|
2003-01-08 00:22:27 +00:00
|
|
|
/* Set up to fetch one row forward */
|
|
|
|
count = 1;
|
|
|
|
forward = true;
|
|
|
|
}
|
2002-12-30 15:31:51 +00:00
|
|
|
}
|
|
|
|
}
|
2002-11-13 00:44:09 +00:00
|
|
|
|
2002-04-15 05:22:04 +00:00
|
|
|
/*
|
2003-03-11 19:40:24 +00:00
|
|
|
* Optimize MOVE BACKWARD ALL into a Rewind.
|
2002-04-15 05:22:04 +00:00
|
|
|
*/
|
2003-03-11 19:40:24 +00:00
|
|
|
if (!forward && count == FETCH_ALL && dest == None)
|
|
|
|
{
|
|
|
|
long result = portal->portalPos;
|
|
|
|
|
|
|
|
if (result > 0 && !portal->atEnd)
|
|
|
|
result--;
|
|
|
|
DoPortalRewind(portal);
|
|
|
|
/* result is bogus if pos had overflowed, but it's best we can do */
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return DoRelativeFetch(portal, forward, count, dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DoRelativeFetch
|
|
|
|
* Do fetch for a simple N-rows-forward-or-backward case.
|
|
|
|
*
|
|
|
|
* count <= 0 is interpreted as a no-op: the destination gets started up
|
|
|
|
* and shut down, but nothing else happens. Also, count == FETCH_ALL is
|
|
|
|
* interpreted as "all rows".
|
|
|
|
*
|
|
|
|
* Caller must already have validated the Portal.
|
|
|
|
*
|
|
|
|
* Returns number of rows processed (suitable for use in result tag)
|
|
|
|
*/
|
|
|
|
static long
|
|
|
|
DoRelativeFetch(Portal portal,
|
|
|
|
bool forward,
|
|
|
|
long count,
|
|
|
|
CommandDest dest)
|
|
|
|
{
|
|
|
|
QueryDesc *queryDesc;
|
|
|
|
EState *estate;
|
|
|
|
ScanDirection direction;
|
|
|
|
QueryDesc temp_queryDesc;
|
2002-04-15 05:22:04 +00:00
|
|
|
|
|
|
|
queryDesc = PortalGetQueryDesc(portal);
|
2002-12-05 15:50:39 +00:00
|
|
|
estate = queryDesc->estate;
|
2002-04-15 05:22:04 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the requested destination is not the same as the query's
|
|
|
|
* original destination, make a temporary QueryDesc with the proper
|
|
|
|
* destination. This supports MOVE, for example, which will pass in
|
|
|
|
* dest = None.
|
|
|
|
*
|
|
|
|
* EXCEPTION: if the query's original dest is RemoteInternal (ie, it's a
|
|
|
|
* binary cursor) and the request is Remote, we do NOT override the
|
|
|
|
* original dest. This is necessary since a FETCH command will pass
|
|
|
|
* dest = Remote, not knowing whether the cursor is binary or not.
|
|
|
|
*/
|
|
|
|
if (dest != queryDesc->dest &&
|
|
|
|
!(queryDesc->dest == RemoteInternal && dest == Remote))
|
|
|
|
{
|
2003-03-11 19:40:24 +00:00
|
|
|
memcpy(&temp_queryDesc, queryDesc, sizeof(QueryDesc));
|
|
|
|
temp_queryDesc.dest = dest;
|
|
|
|
queryDesc = &temp_queryDesc;
|
2002-04-15 05:22:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine which direction to go in, and check to see if we're
|
|
|
|
* already at the end of the available tuples in that direction. If
|
|
|
|
* so, set the direction to NoMovement to avoid trying to fetch any
|
2002-09-04 20:31:48 +00:00
|
|
|
* tuples. (This check exists because not all plan node types are
|
|
|
|
* robust about being called again if they've already returned NULL
|
|
|
|
* once.) Then call the executor (we must not skip this, because the
|
|
|
|
* destination needs to see a setup and shutdown even if no tuples are
|
2003-03-11 19:40:24 +00:00
|
|
|
* available). Finally, update the portal position state depending on
|
2002-09-04 20:31:48 +00:00
|
|
|
* the number of tuples that were retrieved.
|
2002-04-15 05:22:04 +00:00
|
|
|
*/
|
|
|
|
if (forward)
|
|
|
|
{
|
2003-03-11 19:40:24 +00:00
|
|
|
if (portal->atEnd || count <= 0)
|
2002-04-15 05:22:04 +00:00
|
|
|
direction = NoMovementScanDirection;
|
|
|
|
else
|
|
|
|
direction = ForwardScanDirection;
|
|
|
|
|
2003-03-11 19:40:24 +00:00
|
|
|
/* In the executor, zero count processes all rows */
|
|
|
|
if (count == FETCH_ALL)
|
2003-01-08 00:22:27 +00:00
|
|
|
count = 0;
|
2002-04-15 05:22:04 +00:00
|
|
|
|
2003-01-08 00:22:27 +00:00
|
|
|
ExecutorRun(queryDesc, direction, count);
|
|
|
|
|
|
|
|
if (direction != NoMovementScanDirection)
|
|
|
|
{
|
2003-03-11 19:40:24 +00:00
|
|
|
long oldPos;
|
|
|
|
|
2003-01-08 00:22:27 +00:00
|
|
|
if (estate->es_processed > 0)
|
2003-03-11 19:40:24 +00:00
|
|
|
portal->atStart = false; /* OK to go backward now */
|
|
|
|
if (count == 0 ||
|
|
|
|
(unsigned long) estate->es_processed < (unsigned long) count)
|
2003-01-08 00:22:27 +00:00
|
|
|
portal->atEnd = true; /* we retrieved 'em all */
|
2003-03-11 19:40:24 +00:00
|
|
|
oldPos = portal->portalPos;
|
|
|
|
portal->portalPos += estate->es_processed;
|
|
|
|
/* portalPos doesn't advance when we fall off the end */
|
|
|
|
if (portal->portalPos < oldPos)
|
|
|
|
portal->posOverflow = true;
|
2003-01-08 00:22:27 +00:00
|
|
|
}
|
2002-04-15 05:22:04 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-03-10 03:53:52 +00:00
|
|
|
if (!portal->backwardOK)
|
2003-03-11 19:40:24 +00:00
|
|
|
elog(ERROR, "Cursor can only scan forward"
|
2003-03-10 03:53:52 +00:00
|
|
|
"\n\tDeclare it with SCROLL option to enable backward scan");
|
|
|
|
|
2003-03-11 19:40:24 +00:00
|
|
|
if (portal->atStart || count <= 0)
|
2002-04-15 05:22:04 +00:00
|
|
|
direction = NoMovementScanDirection;
|
|
|
|
else
|
|
|
|
direction = BackwardScanDirection;
|
|
|
|
|
2003-03-11 19:40:24 +00:00
|
|
|
/* In the executor, zero count processes all rows */
|
|
|
|
if (count == FETCH_ALL)
|
2003-01-08 00:22:27 +00:00
|
|
|
count = 0;
|
|
|
|
|
|
|
|
ExecutorRun(queryDesc, direction, count);
|
2002-04-15 05:22:04 +00:00
|
|
|
|
2003-01-08 00:22:27 +00:00
|
|
|
if (direction != NoMovementScanDirection)
|
|
|
|
{
|
2003-03-11 19:40:24 +00:00
|
|
|
if (estate->es_processed > 0 && portal->atEnd)
|
|
|
|
{
|
2003-01-08 00:22:27 +00:00
|
|
|
portal->atEnd = false; /* OK to go forward now */
|
2003-03-11 19:40:24 +00:00
|
|
|
portal->portalPos++; /* adjust for endpoint case */
|
|
|
|
}
|
|
|
|
if (count == 0 ||
|
|
|
|
(unsigned long) estate->es_processed < (unsigned long) count)
|
|
|
|
{
|
2003-01-08 00:22:27 +00:00
|
|
|
portal->atStart = true; /* we retrieved 'em all */
|
2003-03-11 19:40:24 +00:00
|
|
|
portal->portalPos = 0;
|
|
|
|
portal->posOverflow = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
long oldPos;
|
|
|
|
|
|
|
|
oldPos = portal->portalPos;
|
|
|
|
portal->portalPos -= estate->es_processed;
|
|
|
|
if (portal->portalPos > oldPos ||
|
|
|
|
portal->portalPos <= 0)
|
|
|
|
portal->posOverflow = true;
|
|
|
|
}
|
2003-01-08 00:22:27 +00:00
|
|
|
}
|
2002-04-15 05:22:04 +00:00
|
|
|
}
|
|
|
|
|
2003-03-11 19:40:24 +00:00
|
|
|
return estate->es_processed;
|
|
|
|
}
|
2002-04-15 05:22:04 +00:00
|
|
|
|
2003-03-11 19:40:24 +00:00
|
|
|
/*
|
|
|
|
* DoPortalRewind - rewind a Portal to starting point
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
DoPortalRewind(Portal portal)
|
|
|
|
{
|
|
|
|
QueryDesc *queryDesc;
|
2003-03-10 03:53:52 +00:00
|
|
|
|
2003-03-11 19:40:24 +00:00
|
|
|
queryDesc = PortalGetQueryDesc(portal);
|
|
|
|
|
|
|
|
ExecutorRewind(queryDesc);
|
|
|
|
|
|
|
|
portal->atStart = true;
|
|
|
|
portal->atEnd = false;
|
|
|
|
portal->portalPos = 0;
|
|
|
|
portal->posOverflow = false;
|
2002-04-15 05:22:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PerformPortalClose
|
2003-03-10 03:53:52 +00:00
|
|
|
* Close a cursor.
|
2002-04-15 05:22:04 +00:00
|
|
|
*/
|
|
|
|
void
|
2003-03-10 03:53:52 +00:00
|
|
|
PerformPortalClose(char *name)
|
2002-04-15 05:22:04 +00:00
|
|
|
{
|
|
|
|
Portal portal;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get the portal from the portal name
|
|
|
|
*/
|
|
|
|
portal = GetPortalByName(name);
|
|
|
|
if (!PortalIsValid(portal))
|
|
|
|
{
|
|
|
|
elog(WARNING, "PerformPortalClose: portal \"%s\" not found",
|
|
|
|
name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: PortalCleanup is called as a side-effect
|
|
|
|
*/
|
|
|
|
PortalDrop(portal);
|
|
|
|
}
|
2003-03-10 03:53:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PreparePortal
|
|
|
|
*/
|
|
|
|
static Portal
|
|
|
|
PreparePortal(char *portalName)
|
|
|
|
{
|
|
|
|
Portal portal;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for already-in-use portal name.
|
|
|
|
*/
|
|
|
|
portal = GetPortalByName(portalName);
|
|
|
|
if (PortalIsValid(portal))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* XXX Should we raise an error rather than closing the old
|
|
|
|
* portal?
|
|
|
|
*/
|
|
|
|
elog(WARNING, "Closing pre-existing portal \"%s\"",
|
|
|
|
portalName);
|
|
|
|
PortalDrop(portal);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the new portal.
|
|
|
|
*/
|
|
|
|
portal = CreatePortal(portalName);
|
|
|
|
|
|
|
|
return portal;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PortalCleanup
|
|
|
|
*
|
|
|
|
* Clean up a portal when it's dropped. Since this mainly exists to run
|
|
|
|
* ExecutorEnd(), it should not be set as the cleanup hook until we have
|
|
|
|
* called ExecutorStart() on the portal's query.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
PortalCleanup(Portal portal)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* sanity checks
|
|
|
|
*/
|
|
|
|
AssertArg(PortalIsValid(portal));
|
|
|
|
AssertArg(portal->cleanup == PortalCleanup);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* tell the executor to shutdown the query
|
|
|
|
*/
|
|
|
|
ExecutorEnd(PortalGetQueryDesc(portal));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This should be unnecessary since the querydesc should be in the
|
|
|
|
* portal's memory context, but do it anyway for symmetry.
|
|
|
|
*/
|
|
|
|
FreeQueryDesc(PortalGetQueryDesc(portal));
|
|
|
|
}
|