diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c index 735e889dbdb31ccf659f37f958d3a086145e9035..81f68119f89fe9c05f57ea5d65d52296aa64ab7a 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 8aa5b90a9ed8220f9df7032c736a8c229ea01c23..389b455fde73fe7782d8efb495c064f026ca9f1b 100644 --- a/doc/src/sgml/ref/copy.sgml +++ b/doc/src/sgml/ref/copy.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v 1.40 2002/09/21 18:32:54 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v 1.41 2003/03/27 16:51:27 momjian Exp $ PostgreSQL documentation --> @@ -133,9 +133,10 @@ COPY <replaceable class="parameter">table</replaceable> [ ( <replaceable class=" </para> <note> <para> - On a copy in, any data item that matches this string will be stored as - a NULL value, so you should make sure that you use the same string - as you used on copy out. + On a <command>COPY FROM</command>, any data item that matches + this string will be stored as a NULL value, so you should + make sure that you use the same string as you used with + <command>COPY TO</command>. </para> </note> </listitem> @@ -210,7 +211,8 @@ ERROR: <replaceable>reason</replaceable> or write to a file. The file must be accessible to the backend and the name must be specified from the viewpoint of the backend. When <filename>stdin</filename> or <filename>stdout</filename> is - specified, data flows through the client frontend to the backend. + specified, data is transmitted via the connection between the + client frontend and the backend. <tip> <para> @@ -234,8 +236,8 @@ ERROR: <replaceable>reason</replaceable> Notes </title> <para> - <command>COPY</command> can only be used with plain tables, not with - views. + <command>COPY</command> can only be used with plain tables, not + with views. </para> <para> diff --git a/doc/src/sgml/ref/declare.sgml b/doc/src/sgml/ref/declare.sgml index 2801aeeabb2e2dc88c127843140f749d80b0ca7f..f0673c44b67f9c5fbc7e159d5bb4cf1120bdd2cb 100644 --- a/doc/src/sgml/ref/declare.sgml +++ b/doc/src/sgml/ref/declare.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/declare.sgml,v 1.20 2003/03/21 17:11:46 momjian Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/declare.sgml,v 1.21 2003/03/27 16:51:27 momjian Exp $ PostgreSQL documentation --> @@ -21,8 +21,8 @@ PostgreSQL documentation <date>1999-07-20</date> </refsynopsisdivinfo> <synopsis> -DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INSENSITIVE ] [ SCROLL ] - CURSOR FOR <replaceable class="parameter">query</replaceable> +DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] + CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">query</replaceable> [ FOR { READ ONLY | UPDATE [ OF <replaceable class="parameter">column</replaceable> [, ...] ] ] </synopsis> <refsect2 id="R2-SQL-DECLARE-1"> @@ -38,7 +38,8 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS <term><replaceable class="parameter">cursorname</replaceable></term> <listitem> <para> - The name of the cursor to be used in subsequent FETCH operations. + The name of the cursor to be used in subsequent + <command>FETCH</command> operations. </para> </listitem> </varlistentry> @@ -57,8 +58,20 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS <listitem> <para> <acronym>SQL92</acronym> 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. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>NO SCROLL</term> + <listitem> + <para> + Specifies that the cursor cannot be used to retrieve rows in a + nonsequential fashion (e.g., backward). </para> </listitem> </varlistentry> @@ -67,8 +80,33 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS <term>SCROLL</term> <listitem> <para> - 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 + <literal>SCROLL</literal> may impose a slight performance penalty + on the query's execution time. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>WITHOUT HOLD</term> + <listitem> + <para> + Specifies that the cursor cannot be used outside of the + transaction that created it. If neither <literal>WITHOUT + HOLD</literal> nor <literal>WITH HOLD</literal> is specified, + <literal>WITH HOLD</literal> is the default. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>WITH HOLD</term> + <listitem> + <para> + Specifies that the cursor may be used after the transaction + that creates it successfully commits. </para> </listitem> </varlistentry> @@ -124,7 +162,8 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS </para> <para> - The BINARY, INSENSITIVE, and SCROLL keywords may appear in any order. + The <literal>BINARY</literal>, <literal>INSENSITIVE</literal>, + <literal>SCROLL</literal> keywords may appear in any order. </para> </refsect2> @@ -144,7 +183,7 @@ DECLARE CURSOR </computeroutput></term> <listitem> <para> - The message returned if the SELECT is run successfully. + The message returned if the <command>SELECT</command> is run successfully. </para> </listitem> </varlistentry> @@ -155,9 +194,8 @@ WARNING: Closing pre-existing portal "<replaceable class="parameter">cursorname </computeroutput></term> <listitem> <para> - 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. </para> </listitem> </varlistentry> @@ -168,7 +206,9 @@ ERROR: DECLARE CURSOR may only be used in begin/end transaction blocks </computeroutput></term> <listitem> <para> - 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 <literal>WITH HOLD</literal> is not + specified. </para> </listitem> </varlistentry> @@ -193,16 +233,14 @@ ERROR: DECLARE CURSOR may only be used in begin/end transaction blocks </para> <para> - Normal cursors return data in text format, the same as a <command>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 + <command>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. </para> <para> @@ -245,7 +283,9 @@ ERROR: DECLARE CURSOR may only be used in begin/end transaction blocks </title> <para> - Cursors are only available within transactions. Use + If <literal>WITH HOLD</literal> is not specified, the cursor + created by this command can only be used within the current + transaction. Use <xref linkend="sql-begin" endterm="sql-begin-title">, <xref linkend="sql-commit" endterm="sql-commit-title"> and @@ -254,12 +294,25 @@ ERROR: DECLARE CURSOR may only be used in begin/end transaction blocks </para> <para> - The <literal>SCROLL</> option should be specified when defining a cursor - that will be used to fetch backwards. This is required by - <acronym>SQL92</acronym>. However, for compatibility with - earlier versions, <productname>PostgreSQL</productname> will allow - backward fetches without <literal>SCROLL</>, if the cursor's query plan - is simple enough that no extra overhead is needed to support it. + If <literal>WITH HOLD</literal> 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 <literal>WITH HOLD</literal> is closed when an explicit + <command>CLOSE</command> command is issued on it, or the client + connection is terminated. + </para> + + <para> + The <literal>SCROLL</> option should be specified when defining a + cursor that will be used to fetch backwards. This is required by + <acronym>SQL92</acronym>. However, for compatibility with earlier + versions, <productname>PostgreSQL</productname> will allow + backward fetches without <literal>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 <literal>SCROLL</literal>. </para> <para> @@ -271,7 +324,7 @@ ERROR: DECLARE CURSOR may only be used in begin/end transaction blocks However, <application>ecpg</application>, the embedded SQL preprocessor for <productname>PostgreSQL</productname>, supports the <acronym>SQL92</acronym> cursor conventions, including those - involving DECLARE and OPEN statements. + involving <command>DECLARE</command> and <command>OPEN</command> statements. </para> </refsect2> </refsect1> @@ -303,13 +356,21 @@ DECLARE liahona CURSOR SQL92 </title> <para> - <acronym>SQL92</acronym> allows cursors only in embedded <acronym>SQL</acronym> - and in modules. <productname>PostgreSQL</productname> permits cursors to be used - interactively. + <para> + <acronym>SQL92</acronym> allows cursors only in embedded + <acronym>SQL</acronym> and in modules. <productname>PostgreSQL</> + permits cursors to be used interactively. + </para> + + <para> <acronym>SQL92</acronym> allows embedded or modular cursors to - update database information. - All <productname>PostgreSQL</productname> cursors are read only. - The BINARY keyword is a <productname>PostgreSQL</productname> extension. + update database information. All <productname>PostgreSQL</> + cursors are read only. + </para> + + <para> + The <literal>BINARY</literal> keyword is a + <productname>PostgreSQL</productname> extension. </para> </refsect2> </refsect1> diff --git a/doc/src/sgml/ref/fetch.sgml b/doc/src/sgml/ref/fetch.sgml index 8f3244eb39ff51fd270f713c6b6a5af8cd109281..a1f3b13719fae9d3d5d46c422cec4d7ba1346050 100644 --- a/doc/src/sgml/ref/fetch.sgml +++ b/doc/src/sgml/ref/fetch.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/fetch.sgml,v 1.27 2003/03/11 19:40:22 tgl Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/fetch.sgml,v 1.28 2003/03/27 16:51:27 momjian Exp $ PostgreSQL documentation --> @@ -251,8 +251,7 @@ WARNING: PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep </computeroutput></term> <listitem> <para> - If <replaceable class="PARAMETER">cursor</replaceable> is not known. - The cursor must have been declared within the current transaction block. + There is no cursor with the specified name. </para> </listitem> </varlistentry> @@ -326,7 +325,9 @@ WARNING: PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep use any variants of <command>FETCH</> other than <command>FETCH NEXT</> or <command>FETCH FORWARD</> with a positive count. For simple queries <productname>PostgreSQL</productname> 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. </para> <para> @@ -339,16 +340,11 @@ WARNING: PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep </para> <para> - Updating data via a cursor is not supported by - <productname>PostgreSQL</productname>, - 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. - </para> - - <para> - Cursors may only be used inside transaction blocks. + Updating data via a cursor is not supported by + <productname>PostgreSQL</productname>, 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 <command>UPDATE</command> commands to replace data. </para> <para> @@ -357,12 +353,6 @@ WARNING: PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep Use <xref linkend="sql-move" endterm="sql-move-title"> to change cursor position without retrieving data. - Refer to - <xref linkend="sql-begin" endterm="sql-begin-title">, - <xref linkend="sql-commit" endterm="sql-commit-title">, - and - <xref linkend="sql-rollback" endterm="sql-rollback-title"> - for further information about transactions. </para> </refsect2> </refsect1> @@ -379,7 +369,7 @@ WARNING: PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep -- Set up and use a cursor: BEGIN WORK; -DECLARE liahona CURSOR FOR SELECT * FROM films; +DECLARE liahona SCROLL CURSOR FOR SELECT * FROM films; -- Fetch first 5 rows in the cursor liahona: FETCH FORWARD 5 IN liahona; @@ -425,9 +415,10 @@ COMMIT WORK; </title> <para> - <acronym>SQL92</acronym> defines FETCH for use in embedded contexts only. - Therefore, it describes placing the results into explicit variables using - an <literal>INTO</> clause, for example: + <acronym>SQL92</acronym> defines <command>FETCH</command> for use + in embedded contexts only. Therefore, it describes placing the + results into explicit variables using an <literal>INTO</> clause, + for example: <synopsis> FETCH ABSOLUTE <replaceable class="PARAMETER">n</replaceable> @@ -435,16 +426,18 @@ FETCH ABSOLUTE <replaceable class="PARAMETER">n</replaceable> INTO :<replaceable class="PARAMETER">variable</replaceable> [, ...] </synopsis> - <productname>PostgreSQL</productname>'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 + <productname>PostgreSQL</productname>'s use of non-embedded + cursors is non-standard, and so is its practice of returning the + result data as if it were a <command>SELECT</command> result. + Other than this point, <command>FETCH</command> is fully upward-compatible with <acronym>SQL92</acronym>. </para> <para> - The FETCH forms involving FORWARD and BACKWARD (including the forms - FETCH <replaceable class="PARAMETER">count</replaceable> and FETCH ALL, - in which FORWARD is implicit) are <productname>PostgreSQL</productname> + The <command>FETCH</command> forms involving FORWARD and BACKWARD + (including the forms FETCH <replaceable + class="PARAMETER">count</replaceable> and FETCH ALL, in which + FORWARD is implicit) are <productname>PostgreSQL</productname> extensions. </para> diff --git a/doc/src/sgml/ref/move.sgml b/doc/src/sgml/ref/move.sgml index f01ee9d8a587c4df5a6259347459b2d3609858bf..cd6d6aca0fd357ef64ff12f54d980fa31118ce13 100644 --- a/doc/src/sgml/ref/move.sgml +++ b/doc/src/sgml/ref/move.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/move.sgml,v 1.20 2003/03/11 19:40:22 tgl Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/move.sgml,v 1.21 2003/03/27 16:51:27 momjian Exp $ PostgreSQL documentation --> @@ -64,12 +64,6 @@ MOVE [ <replaceable class="PARAMETER">direction</replaceable> { FROM | IN } ] <r Refer to <xref linkend="sql-declare" endterm="sql-declare-title"> to define a cursor. - Refer to - <xref linkend="sql-begin" endterm="sql-begin-title">, - <xref linkend="sql-commit" endterm="sql-commit-title">, - and - <xref linkend="sql-rollback" endterm="sql-rollback-title"> - for further information about transactions. </para> </refsect2> </refsect1> @@ -83,7 +77,7 @@ MOVE [ <replaceable class="PARAMETER">direction</replaceable> { FROM | IN } ] <r <programlisting> 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; <computeroutput> diff --git a/doc/src/sgml/sql.sgml b/doc/src/sgml/sql.sgml index 9e788ff0616f69926282366040d9717682d533e5..89de66680289b0bff927156959d9833eec7052ee 100644 --- a/doc/src/sgml/sql.sgml +++ b/doc/src/sgml/sql.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/sql.sgml,v 1.29 2003/02/19 04:06:28 momjian Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/sql.sgml,v 1.30 2003/03/27 16:51:26 momjian Exp $ --> <chapter id="sql"> @@ -851,7 +851,7 @@ A < B + 3. <para> The most often used command in <acronym>SQL</acronym> is the - SELECT statement, + <command>SELECT</command> statement, used to retrieve data. The syntax is: <synopsis> @@ -881,7 +881,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replac <title>Simple Selects</title> <para> - Here are some simple examples using a SELECT statement: + Here are some simple examples using a <command>SELECT</command> statement: <example> <title id="simple-query">Simple Query with Qualification</title> @@ -905,9 +905,10 @@ SELECT * FROM PART </para> <para> - Using <quote>*</quote> 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 <quote>*</quote> in the <command>SELECT</command> 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: <programlisting> SELECT PNAME, PRICE @@ -924,9 +925,9 @@ SELECT PNAME, PRICE Cam | 25 </programlisting> - Note that the <acronym>SQL</acronym> SELECT corresponds to the - <quote>projection</quote> in relational algebra not to the - <quote>selection</quote> (see <xref linkend="rel-alg" + Note that the <acronym>SQL</acronym> <command>SELECT</command> + corresponds to the <quote>projection</quote> in relational algebra + not to the <quote>selection</quote> (see <xref linkend="rel-alg" endterm="rel-alg"> for more details). </para> @@ -1252,15 +1253,15 @@ select sname, pname from supplier <title id="aggregates-tutorial">Aggregate Operators</title> <para> - <acronym>SQL</acronym> 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). + <acronym>SQL</acronym> 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 <command>SELECT</command> 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). <example> <title id="aggregates-example">Aggregates</title> @@ -1413,11 +1414,12 @@ SELECT S.SNO, S.SNAME, COUNT(SE.PNO) </para> <para> - 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 + <command>SELECT</command> 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. </para> </sect3> @@ -1502,16 +1504,18 @@ SELECT * </para> <para> - 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 + <command>SELECT</command> two times. The first one at the + beginning of the query - we will refer to it as outer + <command>SELECT</command> - and the one in the WHERE clause which + begins a nested query - we will refer to it as inner + <command>SELECT</command>. For every tuple of the outer + <command>SELECT</command> the inner <command>SELECT</command> 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.) </para> <para> @@ -1528,11 +1532,13 @@ SELECT * </para> <para> - 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 + <command>SELECT</command> within the WHERE clause of the inner + <command>SELECT</command>. 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 + <command>SELECT</command>. </para> </example> </para> @@ -1670,7 +1676,7 @@ EXCEPT <para> 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: + <command>CREATE TABLE</command> command is: <synopsis> CREATE TABLE <replaceable class="parameter">table_name</replaceable> @@ -1786,7 +1792,7 @@ CREATE TABLE SELLS <para> To create an index in <acronym>SQL</acronym> - the CREATE INDEX command is used. The syntax is: + the <command>CREATE INDEX</command> command is used. The syntax is: <programlisting> CREATE INDEX <replaceable class="parameter">index_name</replaceable> @@ -1808,10 +1814,11 @@ CREATE INDEX I ON SUPPLIER (SNAME); </para> <para> - 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 <command>SELECT</command> + and decreases in speed of updates. </para> </example> </para> @@ -1916,7 +1923,7 @@ SELECT * FROM London_Suppliers <para> To destroy a table (including all tuples stored in that table) the - DROP TABLE command is used: + <command>DROP TABLE</command> command is used: <programlisting> DROP TABLE <replaceable class="parameter">table_name</replaceable>; @@ -1932,7 +1939,7 @@ DROP TABLE SUPPLIER; </para> <para> - The DROP INDEX command is used to destroy an index: + The <command>DROP INDEX</command> command is used to destroy an index: <programlisting> DROP INDEX <replaceable class="parameter">index_name</replaceable>; @@ -1940,7 +1947,8 @@ DROP INDEX <replaceable class="parameter">index_name</replaceable>; </para> <para> - Finally to destroy a given view use the command DROP VIEW: + Finally to destroy a given view use the command <command>DROP + VIEW</command>: <programlisting> DROP VIEW <replaceable class="parameter">view_name</replaceable>; @@ -1994,7 +2002,7 @@ INSERT INTO SELLS (SNO, PNO) <para> To change one or more attribute values of tuples in a relation the - UPDATE command is used. The syntax is: + <command>UPDATE</command> command is used. The syntax is: <programlisting> UPDATE <replaceable class="parameter">table_name</replaceable> @@ -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 <firstterm>cursor</firstterm>. - After that we can use the FETCH command to + After that we can use the <command>FETCH</command> command to retrieve a tuple and set the cursor to the next tuple. </para> diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 3f172dddfc662c3106f1b334bf6b61980a51a65f..598e3c880e5ed0e0bb345d4a153dcf34ba8d52b1 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 baaf57a70ac0af0206576daf58cb4e57c0970a74..978429c87f7118260520a93bda2bd6a3ddda2026 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 1ba72437ad72669e35829d0ff6367d45335f0bc1..7eabc58d49599c55cef10035f068cf0c567027f6 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 <limits.h> +#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); + 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); + + /* + * If we're preserving a holdable portal, we had better be + * inside the transaction that originally created it. + */ + 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); + } + } + /* - * tell the executor to shutdown the query + * 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. */ - ExecutorEnd(PortalGetQueryDesc(portal)); + { + 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; + } /* - * This should be unnecessary since the querydesc should be in the - * portal's memory context, but do it anyway for symmetry. + * We no longer need the portal's short-term memory context. */ - FreeQueryDesc(PortalGetQueryDesc(portal)); + MemoryContextDelete(PortalGetHeapMemory(portal)); + + PortalGetHeapMemory(portal) = NULL; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 7e3f5d2d2c83545b6c2195b91e9934ab6dac772c..cc49bd6d69e8955d57c446b1458aa582ce341004 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 1266895a5f698f7a11c33159d86a72ca31f47206..485f1e03fa0d924a585240aaa58701c80ac01f48 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 a1c1fdd8ad350e8851b79ec5b4c655513a5b729d..7e084f2830201ca87e795003139208dbbc031f5d 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 31152a3d8552d770570417c0a3060e16b1caa21d..b338c8961e2a20b43cd35a9dfc47b0befd86ac70 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 3603fd9b6800f517233eb6b0d56995eb98800b4c..4bc1671801f5cd84de9ffe4d32e107560e298f0f 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 2566851dccc5eb81ae531f6c5d3c5b2883bc2d93..39968c65e0cffe113c4c7cff6a519470a45f2f20 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 61eed9b4004d11e545a50ea7fb6e7049e284ab53..7184fbf9ba173f19838aea7953dcc68cf0153b1d 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 3486a1e010b15358c7bb0cf7a40c4ce09b677699..64cd5603acdc549351d6896b69b5289bc025cf7a 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 dc8b06e750757f946493811d5b5013ab70078d08..0ee065a9a11785638cccd8a40b0098e085d7b05e 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 <boolean> opt_freeze opt_default opt_recheck %type <defelt> opt_binary opt_oids copy_delimiter -%type <boolean> copy_from +%type <boolean> copy_from opt_hold %type <ival> 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 83608d95edb4999b2ad7b86ddd4bfaa0ed814bad..d62f5668d5d7dc659d2911243d9a1a52a2f8f36e 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 7e78793a404337debb63d234f560d44d9f4faf25..90b185cd5dec24169b6012980b5acccafe5443e2 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 83c97fb75503f528517aa13dc28ece463a9e8070..7607d4186cc340001aaf48437c759589e1fee822 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 516af21a4665d4163e7f3a5690885dfaf3eebdfa..ad9d2327717ed849f4ee1bdaa00c6602f0c513bb 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 48545b15128fde0f0991be58e88e5e3d79c76d85..729137a1b9290c0a3dd6a4e865e16948e5be13fe 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 66ee72718cfa640baf2872608181c4dfd9acc574..e6486d4b16dcc89957f6b437b3e0f4b3944712e2 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 1c28028f5f7e262b3c0d1c2e76d75de54eb79eed..643b7a760181fff3442f8a351503fd3a58ff124f 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 65804d5484d770a3b852e2502b2728f990864d64..89970b2495b3fedfd28fd8fa1b3971fdaac363ce 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 49887f450cb9512fd5f02846320b5fad30e35e44..3d4b235e5629e4b999b8a7236d5d1a33204ca610 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 902483a1fb34f5c47e4c04a588d02575f36d03a1..c456b74d0f6ad4f40d2152f3d31a189420396b49 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 a13cec41ea6827dbc137737e9c15ae6a38f44923..261e6314e91a9a60b613a6df4032330973ffc93f 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 bbad436c049b8e16725363f714c85ef5833355c4..bbf86ef4ca60b03a39af8fd1f0761da1e0309c9f 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 c9ca8547ce2d773dfcc69eec2e571d4c56967811..0201b0684c378c2f21402525fb35edaa36ec378b 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 76fe9fb42870db7adfc671e3d9e09bd573b67ae4..dbc47ef29d61d87862c8b92850e74e80b5c03771 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 d105c0e8dcc076a891c0aec78ae740c975c5e247..d2e05f16c26a06c01962f336ff580ac0b0335abe 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 23c186cead15bc3942c51f66181efaea4fc14619..7520653924e17213d159707fad2defaed72530d3 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 c4e257ef2d07d04d7a5d123eed7cf62d13bccdf4..2df820a30a79c28b39c33f6e11db1bc6e76691a9 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