diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 735e889dbd..81f68119f8 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -853,7 +853,7 @@ get_crosstab_tuplestore(char *sql,
MemoryContext SPIcontext;
/* initialize our tuplestore */
- tupstore = tuplestore_begin_heap(true, SortMem);
+ tupstore = tuplestore_begin_heap(true, false, SortMem);
/* Connect to SPI manager */
if ((ret = SPI_connect()) < 0)
@@ -1124,7 +1124,7 @@ connectby(char *relname,
oldcontext = MemoryContextSwitchTo(per_query_ctx);
/* initialize our tuplestore */
- tupstore = tuplestore_begin_heap(true, SortMem);
+ tupstore = tuplestore_begin_heap(true, false, SortMem);
MemoryContextSwitchTo(oldcontext);
diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
index 8aa5b90a9e..389b455fde 100644
--- a/doc/src/sgml/ref/copy.sgml
+++ b/doc/src/sgml/ref/copy.sgml
@@ -1,5 +1,5 @@
@@ -133,9 +133,10 @@ COPY table [ ( cursorname [ BINARY ] [ INSENSITIVE ] [ SCROLL ]
- CURSOR FOR query
+DECLARE cursorname [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ]
+ CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
[ FOR { READ ONLY | UPDATE [ OF column [, ...] ] ]
@@ -38,7 +38,8 @@ DECLARE cursorname [ BINARY ] [ INS
cursorname
- The name of the cursor to be used in subsequent FETCH operations.
+ The name of the cursor to be used in subsequent
+ FETCH operations.
@@ -57,8 +58,20 @@ DECLARE cursorname [ BINARY ] [ INS
SQL92 keyword indicating that data retrieved
- from the cursor should be unaffected by updates from other processes or cursors.
- By default, all cursors are insensitive. This keyword has no effect.
+ from the cursor should be unaffected by updates from other
+ processes or cursors. By default, all cursors are insensitive.
+ This keyword currently has no effect and is present for
+ compatibility with the SQL standard.
+
+
+
+
+
+ NO SCROLL
+
+
+ Specifies that the cursor cannot be used to retrieve rows in a
+ nonsequential fashion (e.g., backward).
@@ -67,8 +80,33 @@ DECLARE cursorname [ BINARY ] [ INS
SCROLL
- Specifies that the cursor may be used to retrieve rows
- in a nonsequential fashion (e.g., backwards).
+ Specifies that the cursor may be used to retrieve rows in a
+ nonsequential fashion (e.g., backward). Depending upon the
+ complexity of the query's execution plan, specifying
+ SCROLL may impose a slight performance penalty
+ on the query's execution time.
+
+
+
+
+
+ WITHOUT HOLD
+
+
+ Specifies that the cursor cannot be used outside of the
+ transaction that created it. If neither WITHOUT
+ HOLD nor WITH HOLD is specified,
+ WITH HOLD is the default.
+
+
+
+
+
+ WITH HOLD
+
+
+ Specifies that the cursor may be used after the transaction
+ that creates it successfully commits.
@@ -124,7 +162,8 @@ DECLARE cursorname [ BINARY ] [ INS
- The BINARY, INSENSITIVE, and SCROLL keywords may appear in any order.
+ The BINARY, INSENSITIVE,
+ SCROLL keywords may appear in any order.
@@ -144,7 +183,7 @@ DECLARE CURSOR
- The message returned if the SELECT is run successfully.
+ The message returned if the SELECT is run successfully.
@@ -155,9 +194,8 @@ WARNING: Closing pre-existing portal "cursorname
- This message is reported if the same cursor name was already declared
- in the current transaction block. The previous definition is
- discarded.
+ This message is reported if a cursor with the same name already
+ exists. The previous definition is discarded.
@@ -168,7 +206,9 @@ ERROR: DECLARE CURSOR may only be used in begin/end transaction blocks
- This error occurs if the cursor is not declared within a transaction block.
+ This error occurs if the cursor is not declared within a
+ transaction block, and WITH HOLD is not
+ specified.
@@ -193,16 +233,14 @@ ERROR: DECLARE CURSOR may only be used in begin/end transaction blocks
- Normal cursors return data in text format, the same as a SELECT>
- would produce. Since
- data is stored natively in binary format, the system must
- do a conversion to produce the text format. In addition,
- text formats are often larger in size than the corresponding binary format.
- Once the information comes back in text form, the client
- application may need to convert it to a binary format to
- manipulate it.
- BINARY cursors give you back the data in the native binary
- representation.
+ Normal cursors return data in text format, the same as a
+ SELECT> would produce. Since data is stored natively in
+ binary format, the system must do a conversion to produce the text
+ format. In addition, text formats are often larger in size than the
+ corresponding binary format. Once the information comes back in
+ text form, the client application may need to convert it to a
+ binary format to manipulate it. BINARY cursors give you back the
+ data in the native binary representation.
@@ -245,7 +283,9 @@ ERROR: DECLARE CURSOR may only be used in begin/end transaction blocks
- Cursors are only available within transactions. Use
+ If WITH HOLD is not specified, the cursor
+ created by this command can only be used within the current
+ transaction. Use
,
and
@@ -254,12 +294,25 @@ ERROR: DECLARE CURSOR may only be used in begin/end transaction blocks
- The SCROLL> option should be specified when defining a cursor
- that will be used to fetch backwards. This is required by
- SQL92. However, for compatibility with
- earlier versions, PostgreSQL will allow
- backward fetches without SCROLL>, if the cursor's query plan
- is simple enough that no extra overhead is needed to support it.
+ If WITH HOLD is specified and the transaction
+ that created the cursor successfully commits, the cursor can be
+ accessed outside the creating transaction. If the creating
+ transaction is aborted, the cursor is removed. A cursor created
+ with WITH HOLD is closed when an explicit
+ CLOSE command is issued on it, or the client
+ connection is terminated.
+
+
+
+ The SCROLL> option should be specified when defining a
+ cursor that will be used to fetch backwards. This is required by
+ SQL92. However, for compatibility with earlier
+ versions, PostgreSQL will allow
+ backward fetches without SCROLL>, if the cursor's query
+ plan is simple enough that no extra overhead is needed to support
+ it. However, application developers are advised not to rely on
+ using backward fetches from a cursor that has not been created
+ with SCROLL.
@@ -271,7 +324,7 @@ ERROR: DECLARE CURSOR may only be used in begin/end transaction blocks
However, ecpg, the
embedded SQL preprocessor for PostgreSQL,
supports the SQL92 cursor conventions, including those
- involving DECLARE and OPEN statements.
+ involving DECLARE and OPEN statements.
@@ -303,13 +356,21 @@ DECLARE liahona CURSOR
SQL92
- SQL92 allows cursors only in embedded SQL
- and in modules. PostgreSQL permits cursors to be used
- interactively.
+
+ SQL92 allows cursors only in embedded
+ SQL and in modules. PostgreSQL>
+ permits cursors to be used interactively.
+
+
+
SQL92 allows embedded or modular cursors to
- update database information.
- All PostgreSQL cursors are read only.
- The BINARY keyword is a PostgreSQL extension.
+ update database information. All PostgreSQL>
+ cursors are read only.
+
+
+
+ The BINARY keyword is a
+ PostgreSQL extension.
diff --git a/doc/src/sgml/ref/fetch.sgml b/doc/src/sgml/ref/fetch.sgml
index 8f3244eb39..a1f3b13719 100644
--- a/doc/src/sgml/ref/fetch.sgml
+++ b/doc/src/sgml/ref/fetch.sgml
@@ -1,5 +1,5 @@
@@ -251,8 +251,7 @@ WARNING: PerformPortalFetch: portal "cursor
- If cursor is not known.
- The cursor must have been declared within the current transaction block.
+ There is no cursor with the specified name.
@@ -326,7 +325,9 @@ WARNING: PerformPortalFetch: portal "cursorFETCH> other than FETCH NEXT>
or FETCH FORWARD> with a positive count. For simple queries
PostgreSQL will allow backwards fetch from
- cursors not declared with SCROLL, but this behavior is best not relied on.
+ cursors not declared with SCROLL, but this behavior is best not
+ relied on. If the cursor is declared with NO SCROLL, no backward
+ fetches are allowed.
@@ -339,16 +340,11 @@ WARNING: PerformPortalFetch: portal "cursor
- Updating data via a cursor is not supported by
- PostgreSQL,
- because mapping cursor updates back to base tables is
- not generally possible, as is also the case with VIEW updates.
- Consequently,
- users must issue explicit UPDATE commands to replace data.
-
-
-
- Cursors may only be used inside transaction blocks.
+ Updating data via a cursor is not supported by
+ PostgreSQL, because mapping cursor
+ updates back to base tables is not generally possible, as is also
+ the case with view updates. Consequently, users must issue
+ explicit UPDATE commands to replace data.
@@ -357,12 +353,6 @@ WARNING: PerformPortalFetch: portal "cursor
to change cursor position without retrieving data.
- Refer to
- ,
- ,
- and
-
- for further information about transactions.
@@ -379,7 +369,7 @@ WARNING: PerformPortalFetch: portal "cursor
- SQL92 defines FETCH for use in embedded contexts only.
- Therefore, it describes placing the results into explicit variables using
- an INTO> clause, for example:
+ SQL92 defines FETCH for use
+ in embedded contexts only. Therefore, it describes placing the
+ results into explicit variables using an INTO> clause,
+ for example:
FETCH ABSOLUTE n
@@ -435,16 +426,18 @@ FETCH ABSOLUTE n
INTO :variable [, ...]
- PostgreSQL's use of non-embedded cursors
- is non-standard, and so is its practice of returning the result data
- as if it were a SELECT result. Other than this point, FETCH is fully
+ PostgreSQL's use of non-embedded
+ cursors is non-standard, and so is its practice of returning the
+ result data as if it were a SELECT result.
+ Other than this point, FETCH is fully
upward-compatible with SQL92.
- The FETCH forms involving FORWARD and BACKWARD (including the forms
- FETCH count and FETCH ALL,
- in which FORWARD is implicit) are PostgreSQL
+ The FETCH forms involving FORWARD and BACKWARD
+ (including the forms FETCH count and FETCH ALL, in which
+ FORWARD is implicit) are PostgreSQL
extensions.
diff --git a/doc/src/sgml/ref/move.sgml b/doc/src/sgml/ref/move.sgml
index f01ee9d8a5..cd6d6aca0f 100644
--- a/doc/src/sgml/ref/move.sgml
+++ b/doc/src/sgml/ref/move.sgml
@@ -1,5 +1,5 @@
@@ -64,12 +64,6 @@ MOVE [ direction { FROM | IN } ]
to define a cursor.
- Refer to
- ,
- ,
- and
-
- for further information about transactions.
@@ -83,7 +77,7 @@ MOVE [ direction { FROM | IN } ]
BEGIN WORK;
-DECLARE liahona CURSOR FOR SELECT * FROM films;
+DECLARE liahona CURSOR FOR SELECT * FROM films;
-- Skip first 5 rows:
MOVE FORWARD 5 IN liahona;
diff --git a/doc/src/sgml/sql.sgml b/doc/src/sgml/sql.sgml
index 9e788ff061..89de666802 100644
--- a/doc/src/sgml/sql.sgml
+++ b/doc/src/sgml/sql.sgml
@@ -1,5 +1,5 @@
@@ -851,7 +851,7 @@ A < B + 3.
The most often used command in SQL is the
- SELECT statement,
+ SELECT statement,
used to retrieve data. The syntax is:
@@ -881,7 +881,7 @@ SELECT [ ALL | DISTINCT [ ON ( expressionSimple Selects
- Here are some simple examples using a SELECT statement:
+ Here are some simple examples using a SELECT statement:
Simple Query with Qualification
@@ -905,9 +905,10 @@ SELECT * FROM PART
- Using *
in the SELECT statement will deliver all attributes from
- the table. If we want to retrieve only the attributes PNAME and PRICE
- from table PART we use the statement:
+ Using *
in the SELECT statement
+ will deliver all attributes from the table. If we want to retrieve
+ only the attributes PNAME and PRICE from table PART we use the
+ statement:
SELECT PNAME, PRICE
@@ -924,9 +925,9 @@ SELECT PNAME, PRICE
Cam | 25
- Note that the SQL SELECT corresponds to the
- projection
in relational algebra not to the
- selection
(see SQL SELECT
+ corresponds to the projection
in relational algebra
+ not to the selection
(see for more details).
@@ -1252,15 +1253,15 @@ select sname, pname from supplier
Aggregate Operators
- SQL provides aggregate operators
- (e.g. AVG, COUNT, SUM, MIN, MAX) that
- take an expression as argument. The expression is evaluated at
- each row that satisfies the WHERE clause, and the aggregate operator
- is calculated over this set of input values. Normally, an aggregate
- delivers a single result for a whole SELECT statement. But if
- grouping is specified in the query, then a separate calculation is done
- over the rows of each group, and an aggregate result is delivered per
- group (see next section).
+ SQL provides aggregate operators (e.g. AVG,
+ COUNT, SUM, MIN, MAX) that take an expression as argument. The
+ expression is evaluated at each row that satisfies the WHERE
+ clause, and the aggregate operator is calculated over this set
+ of input values. Normally, an aggregate delivers a single
+ result for a whole SELECT statement. But if
+ grouping is specified in the query, then a separate calculation
+ is done over the rows of each group, and an aggregate result is
+ delivered per group (see next section).
Aggregates
@@ -1413,11 +1414,12 @@ SELECT S.SNO, S.SNAME, COUNT(SE.PNO)
- Also observe that it makes no sense to ask for an aggregate of an
- aggregate, e.g., AVG(MAX(sno)), because a SELECT only does one pass
- of grouping and aggregation. You can get a result of this kind by
- using a temporary table or a sub-SELECT in the FROM clause to
- do the first level of aggregation.
+ Also observe that it makes no sense to ask for an aggregate of
+ an aggregate, e.g., AVG(MAX(sno)), because a
+ SELECT only does one pass of grouping and
+ aggregation. You can get a result of this kind by using a
+ temporary table or a sub-SELECT in the FROM clause to do the
+ first level of aggregation.
@@ -1502,16 +1504,18 @@ SELECT *
- When we look at the above query we can see
- the keyword SELECT two times. The first one at the beginning of the
- query - we will refer to it as outer SELECT - and the one in the WHERE
- clause which begins a nested query - we will refer to it as inner
- SELECT. For every tuple of the outer SELECT the inner SELECT has to be
- evaluated. After every evaluation we know the price of the tuple named
- 'Screw' and we can check if the price of the actual tuple is
- greater. (Actually, in this example the inner query need only be
- evaluated once, since it does not depend on the state of the outer
- query.)
+ When we look at the above query we can see the keyword
+ SELECT two times. The first one at the
+ beginning of the query - we will refer to it as outer
+ SELECT - and the one in the WHERE clause which
+ begins a nested query - we will refer to it as inner
+ SELECT. For every tuple of the outer
+ SELECT the inner SELECT has
+ to be evaluated. After every evaluation we know the price of the
+ tuple named 'Screw' and we can check if the price of the actual
+ tuple is greater. (Actually, in this example the inner query need
+ only be evaluated once, since it does not depend on the state of
+ the outer query.)
@@ -1528,11 +1532,13 @@ SELECT *
- In our example the result will be empty because every supplier sells
- at least one part. Note that we use S.SNO from the outer SELECT within
- the WHERE clause of the inner SELECT. Here the subquery must be
- evaluated afresh for each tuple from the outer query, i.e. the value for
- S.SNO is always taken from the current tuple of the outer SELECT.
+ In our example the result will be empty because every supplier
+ sells at least one part. Note that we use S.SNO from the outer
+ SELECT within the WHERE clause of the inner
+ SELECT. Here the subquery must be evaluated
+ afresh for each tuple from the outer query, i.e. the value for
+ S.SNO is always taken from the current tuple of the outer
+ SELECT.
@@ -1670,7 +1676,7 @@ EXCEPT
The most fundamental command for data definition is the
one that creates a new relation (a new table). The syntax of the
- CREATE TABLE command is:
+ CREATE TABLE command is:
CREATE TABLE table_name
@@ -1786,7 +1792,7 @@ CREATE TABLE SELLS
To create an index in SQL
- the CREATE INDEX command is used. The syntax is:
+ the CREATE INDEX command is used. The syntax is:
CREATE INDEX index_name
@@ -1808,10 +1814,11 @@ CREATE INDEX I ON SUPPLIER (SNAME);
- The created index is maintained automatically, i.e. whenever a new tuple
- is inserted into the relation SUPPLIER the index I is adapted. Note
- that the only changes a user can perceive when an index is present
- are increased speed for SELECT and decreases in speed of updates.
+ The created index is maintained automatically, i.e. whenever a new
+ tuple is inserted into the relation SUPPLIER the index I is
+ adapted. Note that the only changes a user can perceive when an
+ index is present are increased speed for SELECT
+ and decreases in speed of updates.
@@ -1916,7 +1923,7 @@ SELECT * FROM London_Suppliers
To destroy a table (including all tuples stored in that table) the
- DROP TABLE command is used:
+ DROP TABLE command is used:
DROP TABLE table_name;
@@ -1932,7 +1939,7 @@ DROP TABLE SUPPLIER;
- The DROP INDEX command is used to destroy an index:
+ The DROP INDEX command is used to destroy an index:
DROP INDEX index_name;
@@ -1940,7 +1947,8 @@ DROP INDEX index_name;
- Finally to destroy a given view use the command DROP VIEW:
+ Finally to destroy a given view use the command DROP
+ VIEW:
DROP VIEW view_name;
@@ -1994,7 +2002,7 @@ INSERT INTO SELLS (SNO, PNO)
To change one or more attribute values of tuples in a relation the
- UPDATE command is used. The syntax is:
+ UPDATE command is used. The syntax is:
UPDATE table_name
@@ -2126,7 +2134,7 @@ DELETE FROM SUPPLIER
need a mechanism to access every single tuple of the set of tuples
returned by a SELECT statement. This mechanism can be provided by
declaring a cursor.
- After that we can use the FETCH command to
+ After that we can use the FETCH command to
retrieve a tuple and set the cursor to the next tuple.
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 3f172dddfc..598e3c880e 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.144 2003/03/21 04:33:15 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.145 2003/03/27 16:51:27 momjian Exp $
*
* NOTES
* Transaction aborts can now occur two ways:
@@ -926,7 +926,7 @@ CommitTransaction(void)
* access, and in fact could still cause an error...)
*/
- AtEOXact_portals();
+ AtEOXact_portals(true);
/* handle commit for large objects [ PA, 7/17/98 ] */
/* XXX probably this does not belong here */
@@ -1057,7 +1057,7 @@ AbortTransaction(void)
* do abort processing
*/
DeferredTriggerAbortXact();
- AtEOXact_portals();
+ AtEOXact_portals(false);
lo_commit(false); /* 'false' means it's abort */
AtAbort_Notify();
AtEOXact_UpdatePasswordFile(false);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index baaf57a70a..978429c87f 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1,15 +1,14 @@
/*-------------------------------------------------------------------------
*
* copy.c
- * COPY command.
- *
+ * Implements the COPY utility command.
*
* 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/commands/copy.c,v 1.189 2003/02/03 21:15:43 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.190 2003/03/27 16:51:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -50,6 +49,9 @@
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#define OCTVALUE(c) ((c) - '0')
+/*
+ * Represents the type of data returned by CopyReadAttribute()
+ */
typedef enum CopyReadResult
{
NORMAL_ATTR,
@@ -1311,7 +1313,7 @@ GetTypeElement(Oid type)
* *result is set to indicate what terminated the read:
* NORMAL_ATTR: column delimiter
* END_OF_LINE: newline
- * END_OF_FILE: EOF indication
+ * END_OF_FILE: EOF indicator
* In all cases, the string read up to the terminator is returned.
*
* Note: This function does not care about SQL NULL values -- it
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 1ba72437ad..7eabc58d49 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.10 2003/03/11 19:40:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.11 2003/03/27 16:51:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,18 +17,23 @@
#include
+#include "miscadmin.h"
#include "commands/portalcmds.h"
#include "executor/executor.h"
#include "optimizer/planner.h"
#include "rewrite/rewriteHandler.h"
-
+#include "utils/memutils.h"
static long DoRelativeFetch(Portal portal,
bool forward,
long count,
CommandDest dest);
+static long DoRelativeStoreFetch(Portal portal,
+ bool forward,
+ long count,
+ CommandDest dest);
static void DoPortalRewind(Portal portal);
-static Portal PreparePortal(char *portalName);
+static Portal PreparePortal(DeclareCursorStmt *stmt);
/*
@@ -46,8 +51,15 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
char *cursorName;
QueryDesc *queryDesc;
- /* Check for invalid context (must be in transaction block) */
- RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
+ /*
+ * If this is a non-holdable cursor, we ensure that this statement
+ * has been executed inside a transaction block (or else, it would
+ * have no user-visible effect).
+ *
+ * XXX: surely there is a better way to check this?
+ */
+ if (!(stmt->options & CURSOR_OPT_HOLD))
+ RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
/*
* The query has been through parse analysis, but not rewriting or
@@ -76,7 +88,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
/*
* Create a portal and copy the query and plan into its memory context.
*/
- portal = PreparePortal(stmt->portalname);
+ portal = PreparePortal(stmt);
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
query = copyObject(query);
@@ -130,6 +142,7 @@ PerformPortalFetch(FetchStmt *stmt,
portal = GetPortalByName(stmt->portalname);
if (!PortalIsValid(portal))
{
+ /* FIXME: shouldn't this be an ERROR? */
elog(WARNING, "PerformPortalFetch: portal \"%s\" not found",
stmt->portalname);
return;
@@ -343,6 +356,9 @@ DoRelativeFetch(Portal portal,
ScanDirection direction;
QueryDesc temp_queryDesc;
+ if (portal->holdStore)
+ return DoRelativeStoreFetch(portal, forward, count, dest);
+
queryDesc = PortalGetQueryDesc(portal);
estate = queryDesc->estate;
@@ -407,7 +423,7 @@ DoRelativeFetch(Portal portal,
}
else
{
- if (!portal->backwardOK)
+ if (portal->scrollType == DISABLE_SCROLL)
elog(ERROR, "Cursor can only scan forward"
"\n\tDeclare it with SCROLL option to enable backward scan");
@@ -452,17 +468,85 @@ DoRelativeFetch(Portal portal,
return estate->es_processed;
}
+/*
+ * DoRelativeStoreFetch
+ * Do fetch for a simple N-rows-forward-or-backward case, getting
+ * the results from the portal's tuple store.
+ */
+static long
+DoRelativeStoreFetch(Portal portal,
+ bool forward,
+ long count,
+ CommandDest dest)
+{
+ DestReceiver *destfunc;
+ QueryDesc *queryDesc = portal->queryDesc;
+ long rows_fetched = 0;
+
+ if (!forward && portal->scrollType == DISABLE_SCROLL)
+ elog(ERROR, "Cursor can only scan forward"
+ "\n\tDeclare it with SCROLL option to enable backward scan");
+
+ destfunc = DestToFunction(dest);
+ (*destfunc->setup) (destfunc, queryDesc->operation,
+ portal->name, queryDesc->tupDesc);
+
+ for (;;)
+ {
+ HeapTuple tup;
+ bool should_free;
+
+ if (rows_fetched >= count)
+ break;
+ if (portal->atEnd && forward)
+ break;
+ if (portal->atStart && !forward)
+ break;
+
+ tup = tuplestore_getheaptuple(portal->holdStore, forward, &should_free);
+
+ if (tup == NULL)
+ {
+ if (forward)
+ portal->atEnd = true;
+ else
+ portal->atStart = true;
+
+ break;
+ }
+
+ (*destfunc->receiveTuple) (tup, queryDesc->tupDesc, destfunc);
+
+ rows_fetched++;
+ if (forward)
+ portal->portalPos++;
+ else
+ portal->portalPos--;
+
+ if (forward && portal->atStart)
+ portal->atStart = false;
+ if (!forward && portal->atEnd)
+ portal->atEnd = false;
+
+ if (should_free)
+ pfree(tup);
+ }
+
+ (*destfunc->cleanup) (destfunc);
+
+ return rows_fetched;
+}
+
/*
* DoPortalRewind - rewind a Portal to starting point
*/
static void
DoPortalRewind(Portal portal)
{
- QueryDesc *queryDesc;
-
- queryDesc = PortalGetQueryDesc(portal);
-
- ExecutorRewind(queryDesc);
+ if (portal->holdStore)
+ tuplestore_rescan(portal->holdStore);
+ else
+ ExecutorRewind(PortalGetQueryDesc(portal));
portal->atStart = true;
portal->atEnd = false;
@@ -493,22 +577,25 @@ PerformPortalClose(char *name)
/*
* Note: PortalCleanup is called as a side-effect
*/
- PortalDrop(portal);
+ PortalDrop(portal, false);
}
-
/*
* PreparePortal
+ * Given a DECLARE CURSOR statement, returns the Portal data
+ * structure based on that statement that is used to manage the
+ * Portal internally. If a portal with specified name already
+ * exists, it is replaced.
*/
static Portal
-PreparePortal(char *portalName)
+PreparePortal(DeclareCursorStmt *stmt)
{
Portal portal;
/*
* Check for already-in-use portal name.
*/
- portal = GetPortalByName(portalName);
+ portal = GetPortalByName(stmt->portalname);
if (PortalIsValid(portal))
{
/*
@@ -516,19 +603,30 @@ PreparePortal(char *portalName)
* portal?
*/
elog(WARNING, "Closing pre-existing portal \"%s\"",
- portalName);
- PortalDrop(portal);
+ stmt->portalname);
+ PortalDrop(portal, false);
}
/*
* Create the new portal.
*/
- portal = CreatePortal(portalName);
+ portal = CreatePortal(stmt->portalname);
+
+ /*
+ * Modify the newly created portal based on the options specified in
+ * the DECLARE CURSOR statement.
+ */
+ if (stmt->options & CURSOR_OPT_SCROLL)
+ portal->scrollType = ENABLE_SCROLL;
+ else if (stmt->options & CURSOR_OPT_NO_SCROLL)
+ portal->scrollType = DISABLE_SCROLL;
+
+ if (stmt->options & CURSOR_OPT_HOLD)
+ portal->holdOpen = true;
return portal;
}
-
/*
* PortalCleanup
*
@@ -545,14 +643,128 @@ PortalCleanup(Portal portal)
AssertArg(PortalIsValid(portal));
AssertArg(portal->cleanup == PortalCleanup);
- /*
- * tell the executor to shutdown the query
- */
- ExecutorEnd(PortalGetQueryDesc(portal));
+ if (portal->holdStore)
+ tuplestore_end(portal->holdStore);
+ else
+ ExecutorEnd(PortalGetQueryDesc(portal));
+
+}
+
+/*
+ * PersistHoldablePortal
+ *
+ * Prepare the specified Portal for access outside of the current
+ * transaction. When this function returns, all future accesses to the
+ * portal must be done via the Tuplestore (not by invoking the
+ * executor).
+ */
+void
+PersistHoldablePortal(Portal portal)
+{
+ MemoryContext oldcxt;
+ QueryDesc *queryDesc = PortalGetQueryDesc(portal);
/*
- * This should be unnecessary since the querydesc should be in the
- * portal's memory context, but do it anyway for symmetry.
+ * If we're preserving a holdable portal, we had better be
+ * inside the transaction that originally created it.
*/
- FreeQueryDesc(PortalGetQueryDesc(portal));
+ Assert(portal->createXact == GetCurrentTransactionId());
+ Assert(portal->holdStore == NULL);
+
+ /*
+ * This context is used to store portal data that needs to persist
+ * between transactions.
+ */
+ oldcxt = MemoryContextSwitchTo(portal->holdContext);
+
+ /* XXX: Should SortMem be used for this? */
+ portal->holdStore = tuplestore_begin_heap(true, true, SortMem);
+
+ /* Set the destination to output to the tuplestore */
+ queryDesc->dest = Tuplestore;
+
+ /*
+ * Rewind the executor: we need to store the entire result set in
+ * the tuplestore, so that subsequent backward FETCHs can be
+ * processed.
+ */
+ ExecutorRewind(queryDesc);
+
+ /* Fetch the result set into the tuplestore */
+ ExecutorRun(queryDesc, ForwardScanDirection, 0);
+
+ /*
+ * Reset the position in the result set: ideally, this could be
+ * implemented by just skipping straight to the tuple # that we need
+ * to be at, but the tuplestore API doesn't support that. So we
+ * start at the beginning of the tuplestore and iterate through it
+ * until we reach where we need to be.
+ */
+ if (!portal->atEnd)
+ {
+ int store_pos = 0;
+ bool should_free;
+
+ tuplestore_rescan(portal->holdStore);
+
+ while (store_pos < portal->portalPos)
+ {
+ HeapTuple tmp = tuplestore_gettuple(portal->holdStore,
+ true, &should_free);
+
+ if (tmp == NULL)
+ elog(ERROR,
+ "PersistHoldablePortal: unexpected end of tuple stream");
+
+ store_pos++;
+
+ /*
+ * This could probably be optimized by creating and then
+ * deleting a separate memory context for this series of
+ * operations.
+ */
+ if (should_free)
+ pfree(tmp);
+ }
+ }
+
+ /*
+ * The current Portal structure contains some data that will be
+ * needed by the holdable cursor, but it has been allocated in a
+ * memory context that is not sufficiently long-lived: we need to
+ * copy it into the portal's long-term memory context.
+ */
+ {
+ TupleDesc tupDescCopy;
+ QueryDesc *queryDescCopy;
+
+ /*
+ * We need to use this order as ExecutorEnd invalidates the
+ * queryDesc's tuple descriptor
+ */
+ tupDescCopy = CreateTupleDescCopy(queryDesc->tupDesc);
+
+ ExecutorEnd(queryDesc);
+
+ queryDescCopy = palloc(sizeof(*queryDescCopy));
+
+ /*
+ * This doesn't copy all the dependant data in the QueryDesc,
+ * but that's okay -- the only complex field we need to keep is
+ * the query's tupledesc, which we've copied ourselves.
+ */
+ memcpy(queryDescCopy, queryDesc, sizeof(*queryDesc));
+
+ FreeQueryDesc(queryDesc);
+
+ queryDescCopy->tupDesc = tupDescCopy;
+ portal->queryDesc = queryDescCopy;
+ }
+
+ /*
+ * We no longer need the portal's short-term memory context.
+ */
+ MemoryContextDelete(PortalGetHeapMemory(portal));
+
+ PortalGetHeapMemory(portal) = NULL;
}
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 7e3f5d2d2c..cc49bd6d69 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -4,7 +4,7 @@
# Makefile for executor
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.20 2003/01/10 23:54:24 tgl Exp $
+# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.21 2003/03/27 16:51:27 momjian Exp $
#
#-------------------------------------------------------------------------
@@ -18,7 +18,7 @@ OBJS = execAmi.o execGrouping.o execJunk.o execMain.o \
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \
- nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o spi.o
+ nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o
all: SUBSYS.o
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 1266895a5f..485f1e03fa 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.204 2003/03/27 14:33:11 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.205 2003/03/27 16:51:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -217,8 +217,8 @@ ExecutorRun(QueryDesc *queryDesc,
estate->es_lastoid = InvalidOid;
destfunc = DestToFunction(dest);
- (*destfunc->setup) (destfunc, (int) operation,
- queryDesc->portalName, queryDesc->tupDesc);
+ (*destfunc->setup) (destfunc, operation, queryDesc->portalName,
+ queryDesc->tupDesc);
/*
* run plan
@@ -421,15 +421,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
}
}
-
-/* ===============================================================
- * ===============================================================
- static routines follow
- * ===============================================================
- * ===============================================================
- */
-
-
static void
ExecCheckXactReadOnly(Query *parsetree, CmdType operation)
{
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index a1c1fdd8ad..7e084f2830 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.126 2003/03/09 02:19:13 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.127 2003/03/27 16:51:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1054,8 +1054,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
0,
false);
}
- tupstore = tuplestore_begin_heap(true, /* randomAccess */
- SortMem);
+ tupstore = tuplestore_begin_heap(true, false, SortMem);
MemoryContextSwitchTo(oldcontext);
rsinfo.setResult = tupstore;
rsinfo.setDesc = tupdesc;
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 31152a3d85..b338c8961e 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.74 2003/01/10 23:54:24 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.75 2003/03/27 16:51:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -64,7 +64,7 @@ ExecHash(HashState *node)
* buffers are palloc'd in regular executor context.
*/
for (i = 0; i < nbatch; i++)
- hashtable->innerBatchFile[i] = BufFileCreateTemp();
+ hashtable->innerBatchFile[i] = BufFileCreateTemp(false);
}
/*
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 3603fd9b68..4bc1671801 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.48 2003/01/27 20:51:48 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.49 2003/03/27 16:51:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -138,7 +138,7 @@ ExecHashJoin(HashJoinState *node)
* buffers are palloc'd in regular executor context.
*/
for (i = 0; i < hashtable->nbatch; i++)
- hashtable->outerBatchFile[i] = BufFileCreateTemp();
+ hashtable->outerBatchFile[i] = BufFileCreateTemp(false);
}
else if (hashtable == NULL)
return NULL;
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index 2566851dcc..39968c65e0 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.41 2003/03/09 02:19:13 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.42 2003/03/27 16:51:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -62,8 +62,7 @@ ExecMaterial(MaterialState *node)
*/
if (tuplestorestate == NULL)
{
- tuplestorestate = tuplestore_begin_heap(true, /* randomAccess */
- SortMem);
+ tuplestorestate = tuplestore_begin_heap(true, false, SortMem);
node->tuplestorestate = (void *) tuplestorestate;
}
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 61eed9b400..7184fbf9ba 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.88 2003/03/11 19:40:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.89 2003/03/27 16:51:28 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -866,7 +866,7 @@ SPI_cursor_close(Portal portal)
if (!PortalIsValid(portal))
elog(ERROR, "invalid portal in SPI cursor operation");
- PortalDrop(portal);
+ PortalDrop(portal, false);
}
/* =================== private functions =================== */
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 3486a1e010..64cd5603ac 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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.265 2003/03/10 03:53:50 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.266 2003/03/27 16:51:28 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2294,6 +2294,13 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) stmt;
+ /*
+ * Don't allow both SCROLL and NO SCROLL to be specified
+ */
+ if ((stmt->options & CURSOR_OPT_SCROLL) &&
+ (stmt->options & CURSOR_OPT_NO_SCROLL))
+ elog(ERROR, "Both SCROLL and NO SCROLL cannot be specified.");
+
stmt->query = (Node *) transformStmt(pstate, stmt->query,
&extras_before, &extras_after);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dc8b06e750..0ee065a9a1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.408 2003/03/20 18:52:47 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.409 2003/03/27 16:51:28 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -246,7 +246,7 @@ static void doNegateFloat(Value *v);
%type opt_freeze opt_default opt_recheck
%type opt_binary opt_oids copy_delimiter
-%type copy_from
+%type copy_from opt_hold
%type reindex_type drop_type fetch_count
opt_column event comment_type cursor_options
@@ -348,7 +348,7 @@ static void doNegateFloat(Value *v);
GLOBAL GRANT GROUP_P
- HANDLER HAVING HOUR_P
+ HANDLER HAVING HOLD HOUR_P
ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCREMENT
INDEX INHERITS INITIALLY INNER_P INOUT INPUT
@@ -4230,22 +4230,31 @@ UpdateStmt: UPDATE relation_expr
* CURSOR STATEMENTS
*
*****************************************************************************/
-DeclareCursorStmt: DECLARE name cursor_options CURSOR FOR SelectStmt
+DeclareCursorStmt: DECLARE name cursor_options CURSOR opt_hold FOR SelectStmt
{
DeclareCursorStmt *n = makeNode(DeclareCursorStmt);
n->portalname = $2;
n->options = $3;
- n->query = $6;
+ n->query = $7;
+
+ if ($5)
+ n->options |= CURSOR_OPT_HOLD;
+
$$ = (Node *)n;
}
;
cursor_options: /*EMPTY*/ { $$ = 0; }
- | cursor_options BINARY { $$ = $1 | CURSOR_OPT_BINARY; }
+ | cursor_options NO SCROLL { $$ = $1 | CURSOR_OPT_NO_SCROLL; }
| cursor_options SCROLL { $$ = $1 | CURSOR_OPT_SCROLL; }
+ | cursor_options BINARY { $$ = $1 | CURSOR_OPT_BINARY; }
| cursor_options INSENSITIVE { $$ = $1 | CURSOR_OPT_INSENSITIVE; }
;
+opt_hold: /* EMPTY */ { $$ = FALSE; }
+ | WITH HOLD { $$ = TRUE; }
+ | WITHOUT HOLD { $$ = FALSE; }
+
/*****************************************************************************
*
* QUERY:
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 83608d95ed..d62f5668d5 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.136 2003/03/20 07:02:10 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.137 2003/03/27 16:51:28 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -143,6 +143,7 @@ static const ScanKeyword ScanKeywords[] = {
{"group", GROUP_P},
{"handler", HANDLER},
{"having", HAVING},
+ {"hold", HOLD},
{"hour", HOUR_P},
{"ilike", ILIKE},
{"immediate", IMMEDIATE},
diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c
index 7e78793a40..90b185cd5d 100644
--- a/src/backend/storage/file/buffile.c
+++ b/src/backend/storage/file/buffile.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/file/buffile.c,v 1.14 2002/09/05 00:43:07 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/file/buffile.c,v 1.15 2003/03/27 16:51:29 momjian Exp $
*
* NOTES:
*
@@ -64,6 +64,7 @@ struct BufFile
*/
bool isTemp; /* can only add files if this is TRUE */
+ bool isInterTxn; /* keep open over transactions? */
bool dirty; /* does buffer need to be written? */
/*
@@ -118,7 +119,7 @@ extendBufFile(BufFile *file)
File pfile;
Assert(file->isTemp);
- pfile = OpenTemporaryFile();
+ pfile = OpenTemporaryFile(file->isInterTxn);
Assert(pfile >= 0);
file->files = (File *) repalloc(file->files,
@@ -136,16 +137,17 @@ extendBufFile(BufFile *file)
* written to it).
*/
BufFile *
-BufFileCreateTemp(void)
+BufFileCreateTemp(bool interTxn)
{
BufFile *file;
File pfile;
- pfile = OpenTemporaryFile();
+ pfile = OpenTemporaryFile(interTxn);
Assert(pfile >= 0);
file = makeBufFile(pfile);
file->isTemp = true;
+ file->isInterTxn = interTxn;
return file;
}
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 83c97fb755..7607d4186c 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.95 2002/09/02 06:11:42 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.96 2003/03/27 16:51:29 momjian Exp $
*
* NOTES:
*
@@ -112,14 +112,14 @@ int max_files_per_process = 1000;
#define FileUnknownPos (-1L)
+/* these are the assigned bits in fdstate below: */
+#define FD_TEMPORARY (1 << 0)
+#define FD_TXN_TEMPORARY (1 << 1)
+
typedef struct vfd
{
signed short fd; /* current FD, or VFD_CLOSED if none */
unsigned short fdstate; /* bitflags for VFD's state */
-
-/* these are the assigned bits in fdstate: */
-#define FD_TEMPORARY (1 << 0) /* should be unlinked when closed */
-
File nextFree; /* link to next free VFD, if in freelist */
File lruMoreRecently; /* doubly linked recency-of-use list */
File lruLessRecently;
@@ -750,9 +750,15 @@ PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
* This routine takes care of generating an appropriate tempfile name.
* There's no need to pass in fileFlags or fileMode either, since only
* one setting makes any sense for a temp file.
+ *
+ * keepOverTxn: if true, don't close the file at end-of-transaction. In
+ * most cases, you don't want temporary files to outlive the transaction
+ * that created them, so this should be false -- but if you need
+ * "somewhat" temporary storage, this might be useful. In either case,
+ * the file is removed when the File is explicitely closed.
*/
File
-OpenTemporaryFile(void)
+OpenTemporaryFile(bool keepOverTxn)
{
char tempfilepath[128];
File file;
@@ -795,9 +801,13 @@ OpenTemporaryFile(void)
elog(ERROR, "Failed to create temporary file %s", tempfilepath);
}
- /* Mark it for deletion at close or EOXact */
+ /* Mark it for deletion at close */
VfdCache[file].fdstate |= FD_TEMPORARY;
+ /* Mark it for deletion at EOXact */
+ if (!keepOverTxn)
+ VfdCache[file].fdstate |= FD_TXN_TEMPORARY;
+
return file;
}
@@ -1114,6 +1124,7 @@ AtEOXact_Files(void)
for (i = 1; i < SizeVfdCache; i++)
{
if ((VfdCache[i].fdstate & FD_TEMPORARY) &&
+ (VfdCache[i].fdstate & FD_TXN_TEMPORARY) &&
VfdCache[i].fileName != NULL)
FileClose(i);
}
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 516af21a46..ad9d232771 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.50 2003/01/21 22:06:12 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.51 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -29,6 +29,7 @@
#include "postgres.h"
#include "access/printtup.h"
+#include "executor/tstoreReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
@@ -60,9 +61,11 @@ donothingCleanup(DestReceiver *self)
static DestReceiver donothingDR = {
donothingReceive, donothingSetup, donothingCleanup
};
+
static DestReceiver debugtupDR = {
debugtup, debugSetup, donothingCleanup
};
+
static DestReceiver spi_printtupDR = {
spi_printtup, spi_dest_setup, donothingCleanup
};
@@ -98,6 +101,9 @@ DestToFunction(CommandDest dest)
case SPI:
return &spi_printtupDR;
+ case Tuplestore:
+ return tstoreReceiverCreateDR();
+
case None:
return &donothingDR;
}
@@ -122,6 +128,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case None:
case Debug:
+ case Tuplestore:
case SPI:
break;
}
@@ -183,6 +190,7 @@ NullCommand(CommandDest dest)
break;
case Debug:
+ case Tuplestore:
case None:
default:
break;
@@ -213,6 +221,7 @@ ReadyForQuery(CommandDest dest)
break;
case Debug:
+ case Tuplestore:
case None:
default:
break;
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 48545b1512..729137a1b9 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/mcxt.c,v 1.38 2002/12/16 16:22:46 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/mcxt.c,v 1.39 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -36,7 +36,8 @@
MemoryContext CurrentMemoryContext = NULL;
/*
- * Standard top-level contexts
+ * Standard top-level contexts. For a description of the purpose of each
+ * of these contexts, refer to src/backend/utils/mmgr/README
*/
MemoryContext TopMemoryContext = NULL;
MemoryContext ErrorContext = NULL;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 66ee72718c..e6486d4b16 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.53 2003/03/11 19:40:23 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.54 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,8 +20,8 @@
* "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,
+ * 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
@@ -38,7 +38,6 @@
#include "utils/memutils.h"
#include "utils/portal.h"
-
/*
* estimate of the maximum number of open portals a user would have,
* used in initially sizing the PortalHashTable in EnablePortalManager()
@@ -131,8 +130,8 @@ EnablePortalManager(void)
ctl.entrysize = sizeof(PortalHashEnt);
/*
- * use PORTALS_PER_USER, defined in utils/portal.h as a guess of how
- * many hash table entries to create, initially
+ * use PORTALS_PER_USER as a guess of how many hash table entries to
+ * create, initially
*/
PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,
&ctl, HASH_ELEM);
@@ -157,7 +156,9 @@ GetPortalByName(const char *name)
/*
* PortalSetQuery
- * Attaches a "query" to portal.
+ * Attaches a "query" to the specified portal. Note that in the
+ * case of DECLARE CURSOR, some Portal options have already been
+ * set based upon the parsetree of the original DECLARE statement.
*/
void
PortalSetQuery(Portal portal,
@@ -166,9 +167,25 @@ PortalSetQuery(Portal portal,
{
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)
+ {
+ bool backwardPlan;
+
+ backwardPlan = ExecSupportsBackwardScan(queryDesc->plantree);
+
+ if (backwardPlan)
+ portal->scrollType = ENABLE_SCROLL;
+ else
+ portal->scrollType = DISABLE_SCROLL;
+ }
+
portal->queryDesc = queryDesc;
portal->cleanup = cleanup;
- portal->backwardOK = ExecSupportsBackwardScan(queryDesc->plantree);
portal->atStart = true;
portal->atEnd = false; /* allow fetches */
portal->portalPos = 0;
@@ -179,10 +196,8 @@ PortalSetQuery(Portal portal,
* CreatePortal
* Returns a new portal given a name.
*
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if portal name is invalid.
- * "WARNING" if portal name is in use (existing portal is returned!)
+ * An elog(WARNING) is emitted if portal name is in use (existing
+ * portal is returned!)
*/
Portal
CreatePortal(const char *name)
@@ -214,7 +229,11 @@ CreatePortal(const char *name)
/* initialize portal query */
portal->queryDesc = NULL;
portal->cleanup = NULL;
- portal->backwardOK = false;
+ portal->scrollType = DEFAULT_SCROLL;
+ portal->holdOpen = false;
+ portal->holdStore = NULL;
+ portal->holdContext = NULL;
+ portal->createXact = GetCurrentTransactionId();
portal->atStart = true;
portal->atEnd = true; /* disallow fetches until query is set */
portal->portalPos = 0;
@@ -228,17 +247,47 @@ CreatePortal(const char *name)
/*
* PortalDrop
- * Destroys portal.
+ * Destroy the portal.
*
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if portal is invalid.
+ * keepHoldable: if true, holdable portals should not be removed by
+ * this function. More specifically, invoking this function with
+ * keepHoldable = true on a holdable portal prepares the portal for
+ * access outside of its creating transaction.
*/
void
-PortalDrop(Portal portal)
+PortalDrop(Portal portal, bool persistHoldable)
{
AssertArg(PortalIsValid(portal));
+ if (portal->holdOpen && persistHoldable)
+ {
+ /*
+ * We're "dropping" a holdable portal, but what we really need
+ * to do is prepare the portal for access outside of its
+ * creating transaction.
+ */
+
+ /*
+ * Create the memory context that is used for storage of
+ * long-term (cross transaction) data needed by the holdable
+ * portal.
+ */
+ portal->holdContext =
+ AllocSetContextCreate(PortalMemory,
+ "PortalHeapMemory",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+ /*
+ * Note that PersistHoldablePortal() releases any resources used
+ * by the portal that are local to the creating txn.
+ */
+ PersistHoldablePortal(portal);
+
+ return;
+ }
+
/* remove portal from hash table */
PortalHashTableDelete(portal);
@@ -246,8 +295,20 @@ PortalDrop(Portal portal)
if (PointerIsValid(portal->cleanup))
(*portal->cleanup) (portal);
- /* release subsidiary storage */
- MemoryContextDelete(PortalGetHeapMemory(portal));
+ /*
+ * delete short-term memory context; in the case of a holdable
+ * portal, this has already been done
+ */
+ if (PortalGetHeapMemory(portal))
+ MemoryContextDelete(PortalGetHeapMemory(portal));
+
+ /*
+ * delete long-term memory context; in the case of a non-holdable
+ * portal, this context has never been created, so we don't need to
+ * do anything
+ */
+ if (portal->holdContext)
+ MemoryContextDelete(portal->holdContext);
/* release name and portal data (both are in PortalMemory) */
pfree(portal->name);
@@ -255,7 +316,12 @@ PortalDrop(Portal portal)
}
/*
- * Destroy all portals created in the current transaction (ie, all of them).
+ * 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. Only cursors created in the current transaction should be
+ * removed in this fashion.
*
* 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
@@ -264,13 +330,17 @@ PortalDrop(Portal portal)
* references...
*/
void
-AtEOXact_portals(void)
+AtEOXact_portals(bool isCommit)
{
HASH_SEQ_STATUS status;
PortalHashEnt *hentry;
+ TransactionId xact = GetCurrentTransactionId();
hash_seq_init(&status, PortalHashTable);
while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
- PortalDrop(hentry->portal);
+ {
+ if (hentry->portal->createXact == xact)
+ PortalDrop(hentry->portal, isCommit);
+ }
}
diff --git a/src/backend/utils/sort/logtape.c b/src/backend/utils/sort/logtape.c
index 1c28028f5f..643b7a7601 100644
--- a/src/backend/utils/sort/logtape.c
+++ b/src/backend/utils/sort/logtape.c
@@ -64,7 +64,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/sort/logtape.c,v 1.8 2002/06/20 20:29:40 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/sort/logtape.c,v 1.9 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -470,7 +470,7 @@ LogicalTapeSetCreate(int ntapes)
Assert(ntapes > 0);
lts = (LogicalTapeSet *) palloc(sizeof(LogicalTapeSet) +
(ntapes - 1) *sizeof(LogicalTape *));
- lts->pfile = BufFileCreateTemp();
+ lts->pfile = BufFileCreateTemp(false);
lts->nFileBlocks = 0L;
lts->freeBlocksLen = 32; /* reasonable initial guess */
lts->freeBlocks = (long *) palloc(lts->freeBlocksLen * sizeof(long));
diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c
index 65804d5484..89970b2495 100644
--- a/src/backend/utils/sort/tuplestore.c
+++ b/src/backend/utils/sort/tuplestore.c
@@ -36,7 +36,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplestore.c,v 1.11 2003/03/09 02:19:13 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplestore.c,v 1.12 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -65,6 +65,7 @@ struct Tuplestorestate
{
TupStoreStatus status; /* enumerated value as shown above */
bool randomAccess; /* did caller request random access? */
+ bool interTxn; /* keep open through transactions? */
long availMem; /* remaining memory available, in bytes */
BufFile *myfile; /* underlying file, or NULL if none */
@@ -190,7 +191,8 @@ struct Tuplestorestate
static Tuplestorestate *tuplestore_begin_common(bool randomAccess,
- int maxKBytes);
+ bool interTxn,
+ int maxKBytes);
static void dumptuples(Tuplestorestate *state);
static unsigned int getlen(Tuplestorestate *state, bool eofOK);
static void *copytup_heap(Tuplestorestate *state, void *tup);
@@ -205,7 +207,7 @@ static void *readtup_heap(Tuplestorestate *state, unsigned int len);
*/
static Tuplestorestate *
-tuplestore_begin_common(bool randomAccess, int maxKBytes)
+tuplestore_begin_common(bool randomAccess, bool interTxn, int maxKBytes)
{
Tuplestorestate *state;
@@ -213,6 +215,7 @@ tuplestore_begin_common(bool randomAccess, int maxKBytes)
state->status = TSS_INMEM;
state->randomAccess = randomAccess;
+ state->interTxn = interTxn;
state->availMem = maxKBytes * 1024L;
state->myfile = NULL;
@@ -231,10 +234,27 @@ tuplestore_begin_common(bool randomAccess, int maxKBytes)
return state;
}
+/*
+ * tuplestore_begin_heap
+ *
+ * Create a new tuplestore; other types of tuple stores (other than
+ * "heap" tuple stores, for heap tuples) are possible, but not presently
+ * implemented.
+ *
+ * randomAccess: if true, both forward and backward accesses to the
+ * tuple store are allowed.
+ *
+ * interTxn: if true, the files used by on-disk storage persist beyond
+ * the end of the current transaction.
+ *
+ * maxKBytes: how much data to store in memory (any data beyond this
+ * amount is paged to disk).
+ */
Tuplestorestate *
-tuplestore_begin_heap(bool randomAccess, int maxKBytes)
+tuplestore_begin_heap(bool randomAccess, bool interTxn, int maxKBytes)
{
- Tuplestorestate *state = tuplestore_begin_common(randomAccess, maxKBytes);
+ Tuplestorestate *state = tuplestore_begin_common(randomAccess,
+ interTxn, maxKBytes);
state->copytup = copytup_heap;
state->writetup = writetup_heap;
@@ -321,7 +341,7 @@ tuplestore_puttuple(Tuplestorestate *state, void *tuple)
/*
* Nope; time to switch to tape-based operation.
*/
- state->myfile = BufFileCreateTemp();
+ state->myfile = BufFileCreateTemp(state->interTxn);
state->status = TSS_WRITEFILE;
dumptuples(state);
break;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 49887f450c..3d4b235e56 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.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: parsenodes.h,v 1.235 2003/03/20 18:52:48 momjian Exp $
+ * $Id: parsenodes.h,v 1.236 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1210,7 +1210,9 @@ typedef struct CommentStmt
*/
#define CURSOR_OPT_BINARY 0x0001
#define CURSOR_OPT_SCROLL 0x0002
-#define CURSOR_OPT_INSENSITIVE 0x0004
+#define CURSOR_OPT_NO_SCROLL 0x0004
+#define CURSOR_OPT_INSENSITIVE 0x0008
+#define CURSOR_OPT_HOLD 0x0010
typedef struct DeclareCursorStmt
{
diff --git a/src/include/storage/buffile.h b/src/include/storage/buffile.h
index 902483a1fb..c456b74d0f 100644
--- a/src/include/storage/buffile.h
+++ b/src/include/storage/buffile.h
@@ -18,7 +18,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: buffile.h,v 1.12 2002/06/20 20:29:52 momjian Exp $
+ * $Id: buffile.h,v 1.13 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -34,7 +34,7 @@ typedef struct BufFile BufFile;
* prototypes for functions in buffile.c
*/
-extern BufFile *BufFileCreateTemp(void);
+extern BufFile *BufFileCreateTemp(bool interTxn);
extern void BufFileClose(BufFile *file);
extern size_t BufFileRead(BufFile *file, void *ptr, size_t size);
extern size_t BufFileWrite(BufFile *file, void *ptr, size_t size);
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index a13cec41ea..261e6314e9 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.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: fd.h,v 1.36 2002/08/06 02:36:35 tgl Exp $
+ * $Id: fd.h,v 1.37 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -55,7 +55,7 @@ extern int max_files_per_process;
/* Operations on virtual Files --- equivalent to Unix kernel file ops */
extern File FileNameOpenFile(FileName fileName, int fileFlags, int fileMode);
extern File PathNameOpenFile(FileName fileName, int fileFlags, int fileMode);
-extern File OpenTemporaryFile(void);
+extern File OpenTemporaryFile(bool keepOverTxn);
extern void FileClose(File file);
extern void FileUnlink(File file);
extern int FileRead(File file, char *buffer, int amount);
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index bbad436c04..bbf86ef4ca 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -44,7 +44,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: dest.h,v 1.32 2002/09/04 20:31:45 momjian Exp $
+ * $Id: dest.h,v 1.33 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -70,7 +70,8 @@ typedef enum
Remote, /* results sent to frontend process */
RemoteInternal, /* results sent to frontend process in
* internal (binary) form */
- SPI /* results sent to SPI manager */
+ SPI, /* results sent to SPI manager */
+ Tuplestore /* results sent to Tuplestore */
} CommandDest;
/* ----------------
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index c9ca8547ce..0201b0684c 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: portal.h,v 1.39 2003/03/11 19:40:24 tgl Exp $
+ * $Id: portal.h,v 1.40 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,17 +18,45 @@
#include "executor/execdesc.h"
#include "nodes/memnodes.h"
+#include "utils/tuplestore.h"
+/*
+ * We support three kinds of scroll behavior:
+ *
+ * (1) Neither NO SCROLL nor SCROLL was specified: to remain backward
+ * compatible, we allow backward fetches here, unless it would
+ * impose additional runtime overhead to do so.
+ *
+ * (2) NO SCROLL was specified: don't allow any backward fetches.
+ *
+ * (3) SCROLL was specified: allow all kinds of backward fetches, even
+ * if we need to take a slight performance hit to do so.
+ *
+ * Case #1 is converted to #2 or #3 by looking at the query itself and
+ * determining if scrollability can be supported without additional
+ * overhead.
+ */
+typedef enum
+{
+ DEFAULT_SCROLL,
+ DISABLE_SCROLL,
+ ENABLE_SCROLL
+} ScrollType;
typedef struct PortalData *Portal;
typedef struct PortalData
{
char *name; /* Portal's name */
- MemoryContext heap; /* subsidiary memory */
+ MemoryContext heap; /* memory for storing short-term data */
QueryDesc *queryDesc; /* Info about query associated with portal */
void (*cleanup) (Portal); /* Cleanup routine (optional) */
- bool backwardOK; /* is fetch backwards allowed? */
+ ScrollType scrollType; /* Allow backward fetches? */
+ bool holdOpen; /* hold open after txn ends? */
+ TransactionId createXact; /* the xid of the creating txn */
+ Tuplestorestate *holdStore; /* store for holdable cursors */
+ MemoryContext holdContext; /* memory for long-term data */
+
/*
* atStart, atEnd and portalPos indicate the current cursor position.
* portalPos is zero before the first row, N after fetching N'th row of
@@ -58,11 +86,12 @@ typedef struct PortalData
extern void EnablePortalManager(void);
-extern void AtEOXact_portals(void);
+extern void AtEOXact_portals(bool isCommit);
extern Portal CreatePortal(const char *name);
-extern void PortalDrop(Portal portal);
+extern void PortalDrop(Portal portal, bool persistHoldable);
extern Portal GetPortalByName(const char *name);
extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc,
void (*cleanup) (Portal portal));
+extern void PersistHoldablePortal(Portal portal);
#endif /* PORTAL_H */
diff --git a/src/include/utils/tuplestore.h b/src/include/utils/tuplestore.h
index 76fe9fb428..dbc47ef29d 100644
--- a/src/include/utils/tuplestore.h
+++ b/src/include/utils/tuplestore.h
@@ -17,7 +17,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: tuplestore.h,v 1.9 2003/03/09 03:34:10 tgl Exp $
+ * $Id: tuplestore.h,v 1.10 2003/03/27 16:51:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -37,7 +37,8 @@ typedef struct Tuplestorestate Tuplestorestate;
*/
extern Tuplestorestate *tuplestore_begin_heap(bool randomAccess,
- int maxKBytes);
+ bool interTxn,
+ int maxKBytes);
extern void tuplestore_puttuple(Tuplestorestate *state, void *tuple);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index d105c0e8dc..d2e05f16c2 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.83 2003/03/25 03:16:40 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.84 2003/03/27 16:51:29 momjian Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -1757,7 +1757,7 @@ exec_init_tuple_store(PLpgSQL_execstate * estate)
estate->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
- estate->tuple_store = tuplestore_begin_heap(true, SortMem);
+ estate->tuple_store = tuplestore_begin_heap(true, false, SortMem);
MemoryContextSwitchTo(oldcxt);
estate->rettupdesc = rsi->expectedDesc;
diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out
index 23c186cead..7520653924 100644
--- a/src/test/regress/expected/portals.out
+++ b/src/test/regress/expected/portals.out
@@ -1,30 +1,30 @@
--
--- PORTALS
+-- Cursor regression tests
--
BEGIN;
-DECLARE foo1 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo2 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo3 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo4 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo5 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo6 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo7 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo8 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo9 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo10 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo11 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo12 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo13 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo14 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo15 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo16 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo17 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo18 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo19 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo20 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo21 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo22 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo23 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo1 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo2 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo3 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo4 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo5 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo6 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo7 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo8 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo9 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo10 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo11 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo12 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo13 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo14 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo15 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo16 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo17 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo18 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo19 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo20 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo21 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo22 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo23 SCROLL CURSOR FOR SELECT * FROM tenk1;
FETCH 1 in foo1;
unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
@@ -675,4 +675,66 @@ CLOSE foo9;
CLOSE foo10;
CLOSE foo11;
CLOSE foo12;
-end;
+-- is there a reason why we don't close the rest of the open cursors?
+END;
+--
+-- NO SCROLL disallows backward fetching
+--
+BEGIN;
+DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1;
+FETCH 1 FROM foo24;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+ 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx
+(1 row)
+
+FETCH BACKWARD 1 FROM foo24; -- should fail
+ERROR: Cursor can only scan forward
+ Declare it with SCROLL option to enable backward scan
+END;
+--
+-- Cursors outside transaction blocks
+--
+BEGIN;
+DECLARE foo25 SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2;
+FETCH FROM foo25;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+ 8800 | 0 | 0 | 0 | 0 | 0 | 0 | 800 | 800 | 3800 | 8800 | 0 | 1 | MAAAAA | AAAAAA | AAAAxx
+(1 row)
+
+FETCH FROM foo25;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+ 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx
+(1 row)
+
+COMMIT;
+FETCH FROM foo25;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+ 3420 | 2 | 0 | 0 | 0 | 0 | 20 | 420 | 1420 | 3420 | 3420 | 40 | 41 | OBAAAA | CAAAAA | OOOOxx
+(1 row)
+
+FETCH BACKWARD FROM foo25;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+ 1891 | 1 | 1 | 3 | 1 | 11 | 91 | 891 | 1891 | 1891 | 1891 | 182 | 183 | TUAAAA | BAAAAA | HHHHxx
+(1 row)
+
+FETCH ABSOLUTE -1 FROM foo25;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+ 2968 | 9999 | 0 | 0 | 8 | 8 | 68 | 968 | 968 | 2968 | 2968 | 136 | 137 | EKAAAA | PUOAAA | VVVVxx
+(1 row)
+
+CLOSE foo25;
+--
+-- ROLLBACK should close holdable cursors
+--
+BEGIN;
+DECLARE foo26 CURSOR WITH HOLD FOR SELECT * FROM tenk1;
+ROLLBACK;
+-- should fail
+FETCH FROM foo26;
+WARNING: PerformPortalFetch: portal "foo26" not found
diff --git a/src/test/regress/sql/portals.sql b/src/test/regress/sql/portals.sql
index c4e257ef2d..2df820a30a 100644
--- a/src/test/regress/sql/portals.sql
+++ b/src/test/regress/sql/portals.sql
@@ -1,54 +1,54 @@
--
--- PORTALS
+-- Cursor regression tests
--
BEGIN;
-DECLARE foo1 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo1 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo2 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo2 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo3 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo3 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo4 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo4 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo5 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo5 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo6 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo6 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo7 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo7 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo8 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo8 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo9 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo9 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo10 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo10 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo11 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo11 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo12 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo12 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo13 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo13 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo14 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo14 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo15 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo15 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo16 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo16 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo17 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo17 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo18 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo18 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo19 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo19 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo20 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo20 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo21 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo21 SCROLL CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo22 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo22 SCROLL CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo23 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo23 SCROLL CURSOR FOR SELECT * FROM tenk1;
FETCH 1 in foo1;
@@ -166,5 +166,55 @@ CLOSE foo11;
CLOSE foo12;
-end;
+-- is there a reason why we don't close the rest of the open cursors?
+END;
+
+--
+-- NO SCROLL disallows backward fetching
+--
+
+BEGIN;
+
+DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1;
+
+FETCH 1 FROM foo24;
+
+FETCH BACKWARD 1 FROM foo24; -- should fail
+
+END;
+
+--
+-- Cursors outside transaction blocks
+--
+
+BEGIN;
+
+DECLARE foo25 SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2;
+
+FETCH FROM foo25;
+
+FETCH FROM foo25;
+
+COMMIT;
+
+FETCH FROM foo25;
+
+FETCH BACKWARD FROM foo25;
+
+FETCH ABSOLUTE -1 FROM foo25;
+
+CLOSE foo25;
+
+--
+-- ROLLBACK should close holdable cursors
+--
+
+BEGIN;
+
+DECLARE foo26 CURSOR WITH HOLD FOR SELECT * FROM tenk1;
+
+ROLLBACK;
+
+-- should fail
+FETCH FROM foo26;
\ No newline at end of file