mirror of
https://github.com/postgres/postgres.git
synced 2025-09-06 13:46:51 +03:00
Implement SQL92-compatible FIRST, LAST, ABSOLUTE n, RELATIVE n options
for FETCH and MOVE.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.9 2003/03/10 03:53:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.10 2003/03/11 19:40:22 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -23,6 +23,11 @@
|
||||
#include "rewrite/rewriteHandler.h"
|
||||
|
||||
|
||||
static long DoRelativeFetch(Portal portal,
|
||||
bool forward,
|
||||
long count,
|
||||
CommandDest dest);
|
||||
static void DoPortalRewind(Portal portal);
|
||||
static Portal PreparePortal(char *portalName);
|
||||
|
||||
|
||||
@@ -102,9 +107,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
|
||||
* PerformPortalFetch
|
||||
* Execute SQL FETCH or MOVE command.
|
||||
*
|
||||
* name: name of portal
|
||||
* forward: forward or backward fetch?
|
||||
* count: # of tuples to fetch (INT_MAX means "all"; 0 means "refetch")
|
||||
* stmt: parsetree node for command
|
||||
* dest: where to send results
|
||||
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
|
||||
* in which to store a command completion status string.
|
||||
@@ -112,9 +115,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
|
||||
* completionTag may be NULL if caller doesn't want a status string.
|
||||
*/
|
||||
void
|
||||
PerformPortalFetch(char *name,
|
||||
bool forward,
|
||||
long count,
|
||||
PerformPortalFetch(FetchStmt *stmt,
|
||||
CommandDest dest,
|
||||
char *completionTag)
|
||||
{
|
||||
@@ -123,48 +124,150 @@ PerformPortalFetch(char *name,
|
||||
|
||||
/* initialize completion status in case of early exit */
|
||||
if (completionTag)
|
||||
strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0");
|
||||
|
||||
/* sanity checks */
|
||||
if (name == NULL)
|
||||
{
|
||||
elog(WARNING, "PerformPortalFetch: missing portal name");
|
||||
return;
|
||||
}
|
||||
strcpy(completionTag, stmt->ismove ? "MOVE 0" : "FETCH 0");
|
||||
|
||||
/* get the portal from the portal name */
|
||||
portal = GetPortalByName(name);
|
||||
portal = GetPortalByName(stmt->portalname);
|
||||
if (!PortalIsValid(portal))
|
||||
{
|
||||
elog(WARNING, "PerformPortalFetch: portal \"%s\" not found",
|
||||
name);
|
||||
stmt->portalname);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do it */
|
||||
nprocessed = DoPortalFetch(portal, forward, count, dest);
|
||||
nprocessed = DoPortalFetch(portal,
|
||||
stmt->direction,
|
||||
stmt->howMany,
|
||||
stmt->ismove ? None : dest);
|
||||
|
||||
/* Return command status if wanted */
|
||||
if (completionTag)
|
||||
snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
|
||||
(dest == None) ? "MOVE" : "FETCH",
|
||||
stmt->ismove ? "MOVE" : "FETCH",
|
||||
nprocessed);
|
||||
}
|
||||
|
||||
/*
|
||||
* DoPortalFetch
|
||||
* Guts of PerformPortalFetch --- shared with SPI cursor operations
|
||||
* Guts of PerformPortalFetch --- shared with SPI cursor operations.
|
||||
* Caller must already have validated the Portal.
|
||||
*
|
||||
* Returns number of rows processed.
|
||||
* Returns number of rows processed (suitable for use in result tag)
|
||||
*/
|
||||
long
|
||||
DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
|
||||
DoPortalFetch(Portal portal,
|
||||
FetchDirection fdirection,
|
||||
long count,
|
||||
CommandDest dest)
|
||||
{
|
||||
QueryDesc *queryDesc;
|
||||
EState *estate;
|
||||
MemoryContext oldcontext;
|
||||
ScanDirection direction;
|
||||
bool temp_desc = false;
|
||||
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);
|
||||
|
||||
/*
|
||||
* Zero count means to re-fetch the current row, if any (per SQL92)
|
||||
@@ -174,7 +277,7 @@ DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
|
||||
bool on_row;
|
||||
|
||||
/* Are we sitting on a row? */
|
||||
on_row = (portal->atStart == false && portal->atEnd == false);
|
||||
on_row = (!portal->atStart && !portal->atEnd);
|
||||
|
||||
if (dest == None)
|
||||
{
|
||||
@@ -187,14 +290,12 @@ DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
|
||||
* 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
|
||||
* and shut down correctly; so keep going. Further down in the
|
||||
* routine, count == 0 means we will retrieve no row.
|
||||
* and shut down correctly; so keep going. To DoRelativeFetch,
|
||||
* count == 0 means we will retrieve no row.
|
||||
*/
|
||||
if (on_row)
|
||||
{
|
||||
DoPortalFetch(portal,
|
||||
false /* backward */, 1L,
|
||||
None /* throw away output */);
|
||||
DoRelativeFetch(portal, false, 1L, None);
|
||||
/* Set up to fetch one row forward */
|
||||
count = 1;
|
||||
forward = true;
|
||||
@@ -203,9 +304,44 @@ DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
|
||||
}
|
||||
|
||||
/*
|
||||
* switch into the portal context
|
||||
* Optimize MOVE BACKWARD ALL into a Rewind.
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||
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;
|
||||
|
||||
queryDesc = PortalGetQueryDesc(portal);
|
||||
estate = queryDesc->estate;
|
||||
@@ -224,12 +360,9 @@ DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
|
||||
if (dest != queryDesc->dest &&
|
||||
!(queryDesc->dest == RemoteInternal && dest == Remote))
|
||||
{
|
||||
QueryDesc *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
|
||||
|
||||
memcpy(qdesc, queryDesc, sizeof(QueryDesc));
|
||||
qdesc->dest = dest;
|
||||
queryDesc = qdesc;
|
||||
temp_desc = true;
|
||||
memcpy(&temp_queryDesc, queryDesc, sizeof(QueryDesc));
|
||||
temp_queryDesc.dest = dest;
|
||||
queryDesc = &temp_queryDesc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -240,67 +373,103 @@ DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
|
||||
* 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
|
||||
* available). Finally, update the atStart/atEnd state depending on
|
||||
* available). Finally, update the portal position state depending on
|
||||
* the number of tuples that were retrieved.
|
||||
*/
|
||||
if (forward)
|
||||
{
|
||||
if (portal->atEnd || count == 0)
|
||||
if (portal->atEnd || count <= 0)
|
||||
direction = NoMovementScanDirection;
|
||||
else
|
||||
direction = ForwardScanDirection;
|
||||
|
||||
/* In the executor, zero count processes all portal rows */
|
||||
if (count == INT_MAX)
|
||||
/* In the executor, zero count processes all rows */
|
||||
if (count == FETCH_ALL)
|
||||
count = 0;
|
||||
|
||||
ExecutorRun(queryDesc, direction, count);
|
||||
|
||||
if (direction != NoMovementScanDirection)
|
||||
{
|
||||
long oldPos;
|
||||
|
||||
if (estate->es_processed > 0)
|
||||
portal->atStart = false; /* OK to back up now */
|
||||
if (count <= 0 || (long) estate->es_processed < count)
|
||||
portal->atStart = false; /* OK to go backward now */
|
||||
if (count == 0 ||
|
||||
(unsigned long) estate->es_processed < (unsigned long) count)
|
||||
portal->atEnd = true; /* we retrieved 'em all */
|
||||
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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!portal->backwardOK)
|
||||
elog(ERROR, "Cursor cannot scan backwards"
|
||||
elog(ERROR, "Cursor can only scan forward"
|
||||
"\n\tDeclare it with SCROLL option to enable backward scan");
|
||||
|
||||
if (portal->atStart || count == 0)
|
||||
if (portal->atStart || count <= 0)
|
||||
direction = NoMovementScanDirection;
|
||||
else
|
||||
direction = BackwardScanDirection;
|
||||
|
||||
/* In the executor, zero count processes all portal rows */
|
||||
if (count == INT_MAX)
|
||||
/* In the executor, zero count processes all rows */
|
||||
if (count == FETCH_ALL)
|
||||
count = 0;
|
||||
|
||||
ExecutorRun(queryDesc, direction, count);
|
||||
|
||||
if (direction != NoMovementScanDirection)
|
||||
{
|
||||
if (estate->es_processed > 0)
|
||||
if (estate->es_processed > 0 && portal->atEnd)
|
||||
{
|
||||
portal->atEnd = false; /* OK to go forward now */
|
||||
if (count <= 0 || (long) estate->es_processed < count)
|
||||
portal->portalPos++; /* adjust for endpoint case */
|
||||
}
|
||||
if (count == 0 ||
|
||||
(unsigned long) estate->es_processed < (unsigned long) count)
|
||||
{
|
||||
portal->atStart = true; /* we retrieved 'em all */
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up and switch back to old context.
|
||||
*/
|
||||
if (temp_desc)
|
||||
pfree(queryDesc);
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
return estate->es_processed;
|
||||
}
|
||||
|
||||
/*
|
||||
* DoPortalRewind - rewind a Portal to starting point
|
||||
*/
|
||||
static void
|
||||
DoPortalRewind(Portal portal)
|
||||
{
|
||||
QueryDesc *queryDesc;
|
||||
|
||||
queryDesc = PortalGetQueryDesc(portal);
|
||||
|
||||
ExecutorRewind(queryDesc);
|
||||
|
||||
portal->atStart = true;
|
||||
portal->atEnd = false;
|
||||
portal->portalPos = 0;
|
||||
portal->posOverflow = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* PerformPortalClose
|
||||
* Close a cursor.
|
||||
@@ -310,15 +479,6 @@ PerformPortalClose(char *name)
|
||||
{
|
||||
Portal portal;
|
||||
|
||||
/*
|
||||
* sanity checks ... why is this case allowed by the grammar, anyway?
|
||||
*/
|
||||
if (name == NULL)
|
||||
{
|
||||
elog(WARNING, "PerformPortalClose: missing portal name");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* get the portal from the portal name
|
||||
*/
|
||||
|
@@ -26,7 +26,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.201 2003/03/10 03:53:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.202 2003/03/11 19:40:22 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -287,6 +287,42 @@ ExecutorEnd(QueryDesc *queryDesc)
|
||||
queryDesc->planstate = NULL;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecutorRewind
|
||||
*
|
||||
* This routine may be called on an open queryDesc to rewind it
|
||||
* to the start.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecutorRewind(QueryDesc *queryDesc)
|
||||
{
|
||||
EState *estate;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/* sanity checks */
|
||||
Assert(queryDesc != NULL);
|
||||
|
||||
estate = queryDesc->estate;
|
||||
|
||||
Assert(estate != NULL);
|
||||
|
||||
/* It's probably not sensible to rescan updating queries */
|
||||
Assert(queryDesc->operation == CMD_SELECT);
|
||||
|
||||
/*
|
||||
* Switch into per-query memory context
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
|
||||
|
||||
/*
|
||||
* rescan plan
|
||||
*/
|
||||
ExecReScan(queryDesc->planstate, NULL);
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ExecCheckRTPerms
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.87 2003/03/10 03:53:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.88 2003/03/11 19:40:22 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1349,8 +1349,11 @@ _SPI_cursor_operation(Portal portal, bool forward, int count,
|
||||
_SPI_current->tuptable = NULL;
|
||||
|
||||
/* Run the cursor */
|
||||
_SPI_current->processed = DoPortalFetch(portal, forward, (long) count,
|
||||
dest);
|
||||
_SPI_current->processed =
|
||||
DoPortalFetch(portal,
|
||||
forward ? FETCH_FORWARD : FETCH_BACKWARD,
|
||||
(long) count,
|
||||
dest);
|
||||
|
||||
if (dest == SPI && _SPI_checktuples())
|
||||
elog(FATAL, "SPI_fetch: # of processed tuples check failed");
|
||||
|
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.405 2003/03/10 03:53:50 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.406 2003/03/11 19:40:23 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -191,7 +191,7 @@ static void doNegateFloat(Value *v);
|
||||
|
||||
%type <range> qualified_name OptConstrFromTable
|
||||
|
||||
%type <str> opt_id all_Op MathOp opt_name SpecialRuleRelation
|
||||
%type <str> all_Op MathOp opt_name SpecialRuleRelation
|
||||
|
||||
%type <str> iso_level opt_encoding
|
||||
%type <node> grantee
|
||||
@@ -248,12 +248,10 @@ static void doNegateFloat(Value *v);
|
||||
|
||||
%type <boolean> copy_from
|
||||
|
||||
%type <ival> direction reindex_type drop_type
|
||||
%type <ival> reindex_type drop_type fetch_count
|
||||
opt_column event comment_type cursor_options
|
||||
|
||||
%type <ival> fetch_how_many
|
||||
|
||||
%type <node> select_limit_value select_offset_value
|
||||
%type <node> fetch_direction select_limit_value select_offset_value
|
||||
|
||||
%type <list> OptSeqList
|
||||
%type <defelt> OptSeqElem
|
||||
@@ -345,7 +343,7 @@ static void doNegateFloat(Value *v);
|
||||
EACH ELSE ENCODING ENCRYPTED END_P ESCAPE EXCEPT
|
||||
EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
|
||||
|
||||
FALSE_P FETCH FLOAT_P FOR FORCE FOREIGN FORWARD
|
||||
FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
|
||||
FREEZE FROM FULL FUNCTION
|
||||
|
||||
GLOBAL GRANT GROUP_P
|
||||
@@ -361,7 +359,7 @@ static void doNegateFloat(Value *v);
|
||||
|
||||
KEY
|
||||
|
||||
LANCOMPILER LANGUAGE LEADING LEFT LEVEL LIKE LIMIT
|
||||
LANCOMPILER LANGUAGE LAST_P LEADING LEFT LEVEL LIKE LIMIT
|
||||
LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
|
||||
LOCK_P
|
||||
|
||||
@@ -1239,16 +1237,15 @@ opt_drop_behavior:
|
||||
;
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERY :
|
||||
* close <optname>
|
||||
* close <portalname>
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
ClosePortalStmt:
|
||||
CLOSE opt_id
|
||||
CLOSE name
|
||||
{
|
||||
ClosePortalStmt *n = makeNode(ClosePortalStmt);
|
||||
n->portalname = $2;
|
||||
@@ -1256,10 +1253,6 @@ ClosePortalStmt:
|
||||
}
|
||||
;
|
||||
|
||||
opt_id: ColId { $$ = $1; }
|
||||
| /*EMPTY*/ { $$ = NULL; }
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
@@ -2583,61 +2576,17 @@ comment_text:
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERY:
|
||||
* fetch/move [forward | backward] [ # | all ] [ in <portalname> ]
|
||||
* fetch [ forward | backward | absolute | relative ]
|
||||
* [ # | all | next | prior ] [ [ in | from ] <portalname> ]
|
||||
* fetch/move
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
FetchStmt: FETCH direction fetch_how_many from_in name
|
||||
FetchStmt: FETCH fetch_direction from_in name
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
if ($3 < 0)
|
||||
{
|
||||
$3 = -$3;
|
||||
$2 = (($2 == FETCH_FORWARD) ? FETCH_BACKWARD : FETCH_FORWARD);
|
||||
}
|
||||
n->direction = $2;
|
||||
n->howMany = $3;
|
||||
n->portalname = $5;
|
||||
n->ismove = FALSE;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| FETCH fetch_how_many from_in name
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
if ($2 < 0)
|
||||
{
|
||||
n->howMany = -$2;
|
||||
n->direction = FETCH_BACKWARD;
|
||||
}
|
||||
else
|
||||
{
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = $2;
|
||||
}
|
||||
FetchStmt *n = (FetchStmt *) $2;
|
||||
n->portalname = $4;
|
||||
n->ismove = FALSE;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| FETCH direction from_in name
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = $2;
|
||||
n->howMany = 1;
|
||||
n->portalname = $4;
|
||||
n->ismove = FALSE;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| FETCH from_in name
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = 1;
|
||||
n->portalname = $3;
|
||||
n->ismove = FALSE;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| FETCH name
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
@@ -2647,55 +2596,13 @@ FetchStmt: FETCH direction fetch_how_many from_in name
|
||||
n->ismove = FALSE;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| MOVE direction fetch_how_many from_in name
|
||||
| MOVE fetch_direction from_in name
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
if ($3 < 0)
|
||||
{
|
||||
$3 = -$3;
|
||||
$2 = (($2 == FETCH_FORWARD) ? FETCH_BACKWARD : FETCH_FORWARD);
|
||||
}
|
||||
n->direction = $2;
|
||||
n->howMany = $3;
|
||||
n->portalname = $5;
|
||||
n->ismove = TRUE;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| MOVE fetch_how_many from_in name
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
if ($2 < 0)
|
||||
{
|
||||
n->howMany = -$2;
|
||||
n->direction = FETCH_BACKWARD;
|
||||
}
|
||||
else
|
||||
{
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = $2;
|
||||
}
|
||||
FetchStmt *n = (FetchStmt *) $2;
|
||||
n->portalname = $4;
|
||||
n->ismove = TRUE;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| MOVE direction from_in name
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = $2;
|
||||
n->howMany = 1;
|
||||
n->portalname = $4;
|
||||
n->ismove = TRUE;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| MOVE from_in name
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = 1;
|
||||
n->portalname = $3;
|
||||
n->ismove = TRUE;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| MOVE name
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
@@ -2707,27 +2614,121 @@ FetchStmt: FETCH direction fetch_how_many from_in name
|
||||
}
|
||||
;
|
||||
|
||||
direction: FORWARD { $$ = FETCH_FORWARD; }
|
||||
| BACKWARD { $$ = FETCH_BACKWARD; }
|
||||
| RELATIVE { $$ = FETCH_FORWARD; }
|
||||
| ABSOLUTE
|
||||
fetch_direction:
|
||||
/*EMPTY*/
|
||||
{
|
||||
elog(NOTICE,
|
||||
"FETCH / ABSOLUTE not supported, using RELATIVE");
|
||||
$$ = FETCH_FORWARD;
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = 1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| NEXT
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = 1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| PRIOR
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_BACKWARD;
|
||||
n->howMany = 1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| FIRST_P
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_ABSOLUTE;
|
||||
n->howMany = 1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| LAST_P
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_ABSOLUTE;
|
||||
n->howMany = -1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| ABSOLUTE fetch_count
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_ABSOLUTE;
|
||||
n->howMany = $2;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| RELATIVE fetch_count
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_RELATIVE;
|
||||
n->howMany = $2;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| fetch_count
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = $1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| ALL
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = FETCH_ALL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| FORWARD
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = 1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| FORWARD fetch_count
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = $2;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| FORWARD ALL
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_FORWARD;
|
||||
n->howMany = FETCH_ALL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| BACKWARD
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_BACKWARD;
|
||||
n->howMany = 1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| BACKWARD fetch_count
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_BACKWARD;
|
||||
n->howMany = $2;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| BACKWARD ALL
|
||||
{
|
||||
FetchStmt *n = makeNode(FetchStmt);
|
||||
n->direction = FETCH_BACKWARD;
|
||||
n->howMany = FETCH_ALL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
fetch_how_many:
|
||||
fetch_count:
|
||||
Iconst { $$ = $1; }
|
||||
| '-' Iconst { $$ = - $2; }
|
||||
| ALL { $$ = INT_MAX; }
|
||||
| NEXT { $$ = 1; }
|
||||
| PRIOR { $$ = -1; }
|
||||
;
|
||||
|
||||
from_in: IN_P {}
|
||||
| FROM {}
|
||||
from_in: FROM {}
|
||||
| IN_P {}
|
||||
;
|
||||
|
||||
|
||||
@@ -7093,6 +7094,7 @@ unreserved_keyword:
|
||||
| EXPLAIN
|
||||
| EXTERNAL
|
||||
| FETCH
|
||||
| FIRST_P
|
||||
| FORCE
|
||||
| FORWARD
|
||||
| FUNCTION
|
||||
@@ -7115,6 +7117,7 @@ unreserved_keyword:
|
||||
| KEY
|
||||
| LANCOMPILER
|
||||
| LANGUAGE
|
||||
| LAST_P
|
||||
| LEVEL
|
||||
| LISTEN
|
||||
| LOAD
|
||||
@@ -7170,9 +7173,9 @@ unreserved_keyword:
|
||||
| SCROLL
|
||||
| SECOND_P
|
||||
| SECURITY
|
||||
| SESSION
|
||||
| SEQUENCE
|
||||
| SERIALIZABLE
|
||||
| SESSION
|
||||
| SET
|
||||
| SHARE
|
||||
| SHOW
|
||||
@@ -7211,8 +7214,8 @@ unreserved_keyword:
|
||||
| VOLATILE
|
||||
| WITH
|
||||
| WITHOUT
|
||||
| WRITE
|
||||
| WORK
|
||||
| WRITE
|
||||
| YEAR_P
|
||||
| ZONE
|
||||
;
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.134 2003/02/10 04:44:46 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.135 2003/03/11 19:40:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -128,6 +128,7 @@ static const ScanKeyword ScanKeywords[] = {
|
||||
{"extract", EXTRACT},
|
||||
{"false", FALSE_P},
|
||||
{"fetch", FETCH},
|
||||
{"first", FIRST_P},
|
||||
{"float", FLOAT_P},
|
||||
{"for", FOR},
|
||||
{"force", FORCE},
|
||||
@@ -171,6 +172,7 @@ static const ScanKeyword ScanKeywords[] = {
|
||||
{"key", KEY},
|
||||
{"lancompiler", LANCOMPILER},
|
||||
{"language", LANGUAGE},
|
||||
{"last", LAST_P},
|
||||
{"leading", LEADING},
|
||||
{"left", LEFT},
|
||||
{"level", LEVEL},
|
||||
|
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.194 2003/03/10 03:53:51 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.195 2003/03/11 19:40:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -321,15 +321,8 @@ ProcessUtility(Node *parsetree,
|
||||
break;
|
||||
|
||||
case T_FetchStmt:
|
||||
{
|
||||
FetchStmt *stmt = (FetchStmt *) parsetree;
|
||||
|
||||
PerformPortalFetch(stmt->portalname,
|
||||
stmt->direction == FETCH_FORWARD,
|
||||
stmt->howMany,
|
||||
(stmt->ismove) ? None : dest,
|
||||
completionTag);
|
||||
}
|
||||
PerformPortalFetch((FetchStmt *) parsetree, dest,
|
||||
completionTag);
|
||||
break;
|
||||
|
||||
/*
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.52 2003/03/10 03:53:51 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.53 2003/03/11 19:40:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -167,10 +167,12 @@ PortalSetQuery(Portal portal,
|
||||
AssertArg(PortalIsValid(portal));
|
||||
|
||||
portal->queryDesc = queryDesc;
|
||||
portal->backwardOK = ExecSupportsBackwardScan(queryDesc->plantree);
|
||||
portal->atStart = true; /* Allow fetch forward only, to start */
|
||||
portal->atEnd = false;
|
||||
portal->cleanup = cleanup;
|
||||
portal->backwardOK = ExecSupportsBackwardScan(queryDesc->plantree);
|
||||
portal->atStart = true;
|
||||
portal->atEnd = false; /* allow fetches */
|
||||
portal->portalPos = 0;
|
||||
portal->posOverflow = false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -211,10 +213,12 @@ CreatePortal(const char *name)
|
||||
|
||||
/* initialize portal query */
|
||||
portal->queryDesc = NULL;
|
||||
portal->backwardOK = false;
|
||||
portal->atStart = true; /* disallow fetches until query is set */
|
||||
portal->atEnd = true;
|
||||
portal->cleanup = NULL;
|
||||
portal->backwardOK = false;
|
||||
portal->atStart = true;
|
||||
portal->atEnd = true; /* disallow fetches until query is set */
|
||||
portal->portalPos = 0;
|
||||
portal->posOverflow = false;
|
||||
|
||||
/* put portal in table */
|
||||
PortalHashTableInsert(portal);
|
||||
|
Reference in New Issue
Block a user