Commit 7a3e30e6 authored by Tom Lane's avatar Tom Lane

Add INSERT/UPDATE/DELETE RETURNING, with basic docs and regression tests.

plpgsql support to come later.  Along the way, convert execMain's
SELECT INTO support into a DestReceiver, in order to eliminate some ugly
special cases.

Jonah Harris and Tom Lane
parent 5c9e9c0c
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/delete.sgml,v 1.26 2006/01/22 05:20:33 neilc Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/delete.sgml,v 1.27 2006/08/12 02:52:03 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -23,6 +23,7 @@ PostgreSQL documentation ...@@ -23,6 +23,7 @@ PostgreSQL documentation
DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <replaceable class="parameter">alias</replaceable> ] DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <replaceable class="parameter">alias</replaceable> ]
[ USING <replaceable class="PARAMETER">usinglist</replaceable> ] [ USING <replaceable class="PARAMETER">usinglist</replaceable> ]
[ WHERE <replaceable class="PARAMETER">condition</replaceable> ] [ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
[ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ AS <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -59,6 +60,15 @@ DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] ...@@ -59,6 +60,15 @@ DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ]
circumstances. circumstances.
</para> </para>
<para>
The optional <literal>RETURNING</> clause causes <command>DELETE</>
to compute and return value(s) based on each row actually deleted.
Any expression using the table's columns, and/or columns of other
tables mentioned in <literal>USING</literal>, can be computed.
The syntax of the <literal>RETURNING</> list is identical to that of the
output list of <command>SELECT</>.
</para>
<para> <para>
You must have the <literal>DELETE</literal> privilege on the table You must have the <literal>DELETE</literal> privilege on the table
to delete from it, as well as the <literal>SELECT</literal> to delete from it, as well as the <literal>SELECT</literal>
...@@ -130,6 +140,28 @@ DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] ...@@ -130,6 +140,28 @@ DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ]
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">output_expression</replaceable></term>
<listitem>
<para>
An expression to be computed and returned by the <command>DELETE</>
command after each row is deleted. The expression may use any
column names of the <replaceable class="PARAMETER">table</replaceable>
or table(s) listed in <literal>USING</>.
Write <literal>*</> to return all columns.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">output_name</replaceable></term>
<listitem>
<para>
A name to use for a returned column.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
...@@ -148,6 +180,14 @@ DELETE <replaceable class="parameter">count</replaceable> ...@@ -148,6 +180,14 @@ DELETE <replaceable class="parameter">count</replaceable>
class="parameter">condition</replaceable> (this is not considered class="parameter">condition</replaceable> (this is not considered
an error). an error).
</para> </para>
<para>
If the <command>DELETE</> command contains a <literal>RETURNING</>
clause, the result will be similar to that of a <command>SELECT</>
statement containing the columns and values defined in the
<literal>RETURNING</> list, computed over the row(s) deleted by the
command.
</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
...@@ -189,6 +229,13 @@ DELETE FROM films WHERE kind &lt;&gt; 'Musical'; ...@@ -189,6 +229,13 @@ DELETE FROM films WHERE kind &lt;&gt; 'Musical';
Clear the table <literal>films</literal>: Clear the table <literal>films</literal>:
<programlisting> <programlisting>
DELETE FROM films; DELETE FROM films;
</programlisting>
</para>
<para>
Delete completed tasks, returning full details of the deleted rows:
<programlisting>
DELETE FROM tasks WHERE status = 'DONE' RETURNING *;
</programlisting> </programlisting>
</para> </para>
</refsect1> </refsect1>
...@@ -197,10 +244,9 @@ DELETE FROM films; ...@@ -197,10 +244,9 @@ DELETE FROM films;
<title>Compatibility</title> <title>Compatibility</title>
<para> <para>
This command conforms to the SQL standard, except that the This command conforms to the <acronym>SQL</acronym> standard, except
<literal>USING</> clause and the ability to reference other tables that the <literal>USING</literal> and <literal>RETURNING</> clauses
in the <literal>WHERE</> clause are <productname>PostgreSQL</> are <productname>PostgreSQL</productname> extensions.
extensions.
</para> </para>
</refsect1> </refsect1>
</refentry> </refentry>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.30 2005/11/17 22:14:51 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.31 2006/08/12 02:52:03 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -21,7 +21,8 @@ PostgreSQL documentation ...@@ -21,7 +21,8 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) ] INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) ]
{ DEFAULT VALUES | VALUES ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) | <replaceable class="PARAMETER">query</replaceable> } { DEFAULT VALUES | VALUES ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) [, ...] | <replaceable class="PARAMETER">query</replaceable> }
[ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ AS <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -30,8 +31,8 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable ...@@ -30,8 +31,8 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable
<para> <para>
<command>INSERT</command> inserts new rows into a table. <command>INSERT</command> inserts new rows into a table.
One can insert a single row specified by value expressions, One can insert rows specified by value expressions,
or several rows as a result of a query. or rows computed as a result of a query.
</para> </para>
<para> <para>
...@@ -55,6 +56,16 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable ...@@ -55,6 +56,16 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable
automatic type conversion will be attempted. automatic type conversion will be attempted.
</para> </para>
<para>
The optional <literal>RETURNING</> clause causes <command>INSERT</>
to compute and return value(s) based on each row actually inserted.
This is primarily useful for obtaining values that were supplied by
defaults, such as a serial sequence number. However, any expression
using the table's columns is allowed. The syntax of the
<literal>RETURNING</> list is identical to that of the output list
of <command>SELECT</>.
</para>
<para> <para>
You must have <literal>INSERT</literal> privilege to a table in You must have <literal>INSERT</literal> privilege to a table in
order to insert into it. If you use the <replaceable order to insert into it. If you use the <replaceable
...@@ -123,11 +134,33 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable ...@@ -123,11 +134,33 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable
<listitem> <listitem>
<para> <para>
A query (<command>SELECT</command> statement) that supplies the A query (<command>SELECT</command> statement) that supplies the
rows to be inserted. Refer to the <command>SELECT</command> rows to be inserted. Refer to the
<xref linkend="sql-select" endterm="sql-select-title">
statement for a description of the syntax. statement for a description of the syntax.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">output_expression</replaceable></term>
<listitem>
<para>
An expression to be computed and returned by the <command>INSERT</>
command after each row is inserted. The expression may use any
column names of the <replaceable class="PARAMETER">table</replaceable>.
Write <literal>*</> to return all columns of the inserted row(s).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">output_name</replaceable></term>
<listitem>
<para>
A name to use for a returned column.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
...@@ -147,6 +180,14 @@ INSERT <replaceable>oid</replaceable> <replaceable class="parameter">count</repl ...@@ -147,6 +180,14 @@ INSERT <replaceable>oid</replaceable> <replaceable class="parameter">count</repl
<acronym>OID</acronym> assigned to the inserted row. Otherwise <acronym>OID</acronym> assigned to the inserted row. Otherwise
<replaceable class="parameter">oid</replaceable> is zero. <replaceable class="parameter">oid</replaceable> is zero.
</para> </para>
<para>
If the <command>INSERT</> command contains a <literal>RETURNING</>
clause, the result will be similar to that of a <command>SELECT</>
statement containing the columns and values defined in the
<literal>RETURNING</> list, computed over the row(s) inserted by the
command.
</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
...@@ -211,6 +252,16 @@ INSERT INTO tictactoe (game, board[1:3][1:3]) ...@@ -211,6 +252,16 @@ INSERT INTO tictactoe (game, board[1:3][1:3])
-- The subscripts in the above example aren't really needed -- The subscripts in the above example aren't really needed
INSERT INTO tictactoe (game, board) INSERT INTO tictactoe (game, board)
VALUES (2, '{{X," "," "},{" ",O," "},{" ",X," "}}'); VALUES (2, '{{X," "," "},{" ",O," "},{" ",X," "}}');
</programlisting>
</para>
<para>
Insert a single row into table <literal>distributors</literal>, returning
the sequence number generated by the <literal>DEFAULT</literal> clause:
<programlisting>
INSERT INTO distributors (did, dname) VALUES (DEFAULT, 'XYZ Widgets')
RETURNING did;
</programlisting> </programlisting>
</para> </para>
</refsect1> </refsect1>
...@@ -219,7 +270,9 @@ INSERT INTO tictactoe (game, board) ...@@ -219,7 +270,9 @@ INSERT INTO tictactoe (game, board)
<title>Compatibility</title> <title>Compatibility</title>
<para> <para>
<command>INSERT</command> conforms to the SQL standard. The case in <command>INSERT</command> conforms to the SQL standard, except that
the <literal>RETURNING</> clause is a
<productname>PostgreSQL</productname> extension. Also, the case in
which a column name list is omitted, but not all the columns are which a column name list is omitted, but not all the columns are
filled from the <literal>VALUES</> clause or <replaceable>query</>, filled from the <literal>VALUES</> clause or <replaceable>query</>,
is disallowed by the standard. is disallowed by the standard.
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/update.sgml,v 1.37 2006/03/08 22:59:09 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/update.sgml,v 1.38 2006/08/12 02:52:03 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -24,6 +24,7 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep ...@@ -24,6 +24,7 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep
SET <replaceable class="PARAMETER">column</replaceable> = { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] SET <replaceable class="PARAMETER">column</replaceable> = { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...]
[ FROM <replaceable class="PARAMETER">fromlist</replaceable> ] [ FROM <replaceable class="PARAMETER">fromlist</replaceable> ]
[ WHERE <replaceable class="PARAMETER">condition</replaceable> ] [ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
[ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ AS <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -52,6 +53,16 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep ...@@ -52,6 +53,16 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep
circumstances. circumstances.
</para> </para>
<para>
The optional <literal>RETURNING</> clause causes <command>UPDATE</>
to compute and return value(s) based on each row actually updated.
Any expression using the table's columns, and/or columns of other
tables mentioned in <literal>FROM</literal>, can be computed.
The new (post-update) values of the table's columns are used.
The syntax of the <literal>RETURNING</> list is identical to that of the
output list of <command>SELECT</>.
</para>
<para> <para>
You must have the <literal>UPDATE</literal> privilege on the table You must have the <literal>UPDATE</literal> privilege on the table
to update it, as well as the <literal>SELECT</literal> to update it, as well as the <literal>SELECT</literal>
...@@ -147,6 +158,28 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep ...@@ -147,6 +158,28 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">output_expression</replaceable></term>
<listitem>
<para>
An expression to be computed and returned by the <command>UPDATE</>
command after each row is updated. The expression may use any
column names of the <replaceable class="PARAMETER">table</replaceable>
or table(s) listed in <literal>FROM</>.
Write <literal>*</> to return all columns.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">output_name</replaceable></term>
<listitem>
<para>
A name to use for a returned column.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
...@@ -165,6 +198,14 @@ UPDATE <replaceable class="parameter">count</replaceable> ...@@ -165,6 +198,14 @@ UPDATE <replaceable class="parameter">count</replaceable>
class="parameter">condition</replaceable> (this is not considered class="parameter">condition</replaceable> (this is not considered
an error). an error).
</para> </para>
<para>
If the <command>UPDATE</> command contains a <literal>RETURNING</>
clause, the result will be similar to that of a <command>SELECT</>
statement containing the columns and values defined in the
<literal>RETURNING</> list, computed over the row(s) updated by the
command.
</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
...@@ -212,6 +253,16 @@ UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT ...@@ -212,6 +253,16 @@ UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
</programlisting> </programlisting>
</para> </para>
<para>
Perform the same operation and return the updated entries:
<programlisting>
UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
WHERE city = 'San Francisco' AND date = '2003-07-03'
RETURNING temp_lo, temp_hi, prcp;
</programlisting>
</para>
<para> <para>
Increment the sales count of the salesperson who manages the Increment the sales count of the salesperson who manages the
account for Acme Corporation, using the <literal>FROM</literal> account for Acme Corporation, using the <literal>FROM</literal>
...@@ -256,8 +307,8 @@ COMMIT; ...@@ -256,8 +307,8 @@ COMMIT;
<para> <para>
This command conforms to the <acronym>SQL</acronym> standard, except This command conforms to the <acronym>SQL</acronym> standard, except
that the <literal>FROM</literal> clause is a that the <literal>FROM</literal> and <literal>RETURNING</> clauses
<productname>PostgreSQL</productname> extension. are <productname>PostgreSQL</productname> extensions.
</para> </para>
<para> <para>
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.97 2006/07/14 14:52:16 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.98 2006/08/12 02:52:03 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -127,10 +127,10 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo) ...@@ -127,10 +127,10 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
} }
/* /*
* If this is a retrieve, and we are supposed to emit row descriptions, * If we are supposed to emit row descriptions,
* then we send back the tuple descriptor of the tuples. * then send the tuple descriptor of the tuples.
*/ */
if (operation == CMD_SELECT && myState->sendDescrip) if (myState->sendDescrip)
SendRowDescriptionMessage(typeinfo, SendRowDescriptionMessage(typeinfo,
FetchPortalTargetList(portal), FetchPortalTargetList(portal),
portal->formats); portal->formats);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Copyright (c) 2002-2006, PostgreSQL Global Development Group * Copyright (c) 2002-2006, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.59 2006/08/08 01:23:15 momjian Exp $ * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.60 2006/08/12 02:52:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -447,6 +447,10 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt) ...@@ -447,6 +447,10 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
query = (Query *) linitial(stmt->query_list); query = (Query *) linitial(stmt->query_list);
return ExecCleanTypeFromTL(query->targetList, false); return ExecCleanTypeFromTL(query->targetList, false);
case PORTAL_ONE_RETURNING:
query = (Query *) linitial(stmt->query_list);
return ExecCleanTypeFromTL(query->returningList, false);
case PORTAL_UTIL_SELECT: case PORTAL_UTIL_SELECT:
query = (Query *) linitial(stmt->query_list); query = (Query *) linitial(stmt->query_list);
return UtilityTupleDescriptor(query->utilityStmt); return UtilityTupleDescriptor(query->utilityStmt);
...@@ -472,6 +476,7 @@ PreparedStatementReturnsTuples(PreparedStatement *stmt) ...@@ -472,6 +476,7 @@ PreparedStatementReturnsTuples(PreparedStatement *stmt)
switch (ChoosePortalStrategy(stmt->query_list)) switch (ChoosePortalStrategy(stmt->query_list))
{ {
case PORTAL_ONE_SELECT: case PORTAL_ONE_SELECT:
case PORTAL_ONE_RETURNING:
case PORTAL_UTIL_SELECT: case PORTAL_UTIL_SELECT:
return true; return true;
...@@ -499,6 +504,8 @@ FetchPreparedStatementTargetList(PreparedStatement *stmt) ...@@ -499,6 +504,8 @@ FetchPreparedStatementTargetList(PreparedStatement *stmt)
if (strategy == PORTAL_ONE_SELECT) if (strategy == PORTAL_ONE_SELECT)
return ((Query *) linitial(stmt->query_list))->targetList; return ((Query *) linitial(stmt->query_list))->targetList;
if (strategy == PORTAL_ONE_RETURNING)
return ((Query *) linitial(stmt->query_list))->returningList;
if (strategy == PORTAL_UTIL_SELECT) if (strategy == PORTAL_UTIL_SELECT)
{ {
Node *utilityStmt; Node *utilityStmt;
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.153 2006/08/08 01:23:15 momjian Exp $ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.154 2006/08/12 02:52:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -955,6 +955,7 @@ SPI_cursor_open(const char *name, void *plan, ...@@ -955,6 +955,7 @@ SPI_cursor_open(const char *name, void *plan,
PortalStart(portal, paramLI, snapshot); PortalStart(portal, paramLI, snapshot);
Assert(portal->strategy == PORTAL_ONE_SELECT || Assert(portal->strategy == PORTAL_ONE_SELECT ||
portal->strategy == PORTAL_ONE_RETURNING ||
portal->strategy == PORTAL_UTIL_SELECT); portal->strategy == PORTAL_UTIL_SELECT);
/* Return the created portal */ /* Return the created portal */
...@@ -1521,17 +1522,15 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount) ...@@ -1521,17 +1522,15 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
switch (operation) switch (operation)
{ {
case CMD_SELECT: case CMD_SELECT:
res = SPI_OK_SELECT;
if (queryDesc->parsetree->into) /* select into table? */ if (queryDesc->parsetree->into) /* select into table? */
{
res = SPI_OK_SELINTO; res = SPI_OK_SELINTO;
queryDesc->dest = None_Receiver; /* don't output results */
}
else if (queryDesc->dest->mydest != DestSPI) else if (queryDesc->dest->mydest != DestSPI)
{ {
/* Don't return SPI_OK_SELECT if we're discarding result */ /* Don't return SPI_OK_SELECT if we're discarding result */
res = SPI_OK_UTILITY; res = SPI_OK_UTILITY;
} }
else
res = SPI_OK_SELECT;
break; break;
case CMD_INSERT: case CMD_INSERT:
res = SPI_OK_INSERT; res = SPI_OK_INSERT;
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.346 2006/08/10 02:36:28 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.347 2006/08/12 02:52:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1704,6 +1704,7 @@ _copyQuery(Query *from) ...@@ -1704,6 +1704,7 @@ _copyQuery(Query *from)
COPY_NODE_FIELD(rtable); COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(jointree);
COPY_NODE_FIELD(targetList); COPY_NODE_FIELD(targetList);
COPY_NODE_FIELD(returningList);
COPY_NODE_FIELD(groupClause); COPY_NODE_FIELD(groupClause);
COPY_NODE_FIELD(havingQual); COPY_NODE_FIELD(havingQual);
COPY_NODE_FIELD(distinctClause); COPY_NODE_FIELD(distinctClause);
...@@ -1713,6 +1714,7 @@ _copyQuery(Query *from) ...@@ -1713,6 +1714,7 @@ _copyQuery(Query *from)
COPY_NODE_FIELD(rowMarks); COPY_NODE_FIELD(rowMarks);
COPY_NODE_FIELD(setOperations); COPY_NODE_FIELD(setOperations);
COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(returningLists);
return newnode; return newnode;
} }
...@@ -1725,6 +1727,7 @@ _copyInsertStmt(InsertStmt *from) ...@@ -1725,6 +1727,7 @@ _copyInsertStmt(InsertStmt *from)
COPY_NODE_FIELD(relation); COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(cols); COPY_NODE_FIELD(cols);
COPY_NODE_FIELD(selectStmt); COPY_NODE_FIELD(selectStmt);
COPY_NODE_FIELD(returningList);
return newnode; return newnode;
} }
...@@ -1735,8 +1738,9 @@ _copyDeleteStmt(DeleteStmt *from) ...@@ -1735,8 +1738,9 @@ _copyDeleteStmt(DeleteStmt *from)
DeleteStmt *newnode = makeNode(DeleteStmt); DeleteStmt *newnode = makeNode(DeleteStmt);
COPY_NODE_FIELD(relation); COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(whereClause);
COPY_NODE_FIELD(usingClause); COPY_NODE_FIELD(usingClause);
COPY_NODE_FIELD(whereClause);
COPY_NODE_FIELD(returningList);
return newnode; return newnode;
} }
...@@ -1750,6 +1754,7 @@ _copyUpdateStmt(UpdateStmt *from) ...@@ -1750,6 +1754,7 @@ _copyUpdateStmt(UpdateStmt *from)
COPY_NODE_FIELD(targetList); COPY_NODE_FIELD(targetList);
COPY_NODE_FIELD(whereClause); COPY_NODE_FIELD(whereClause);
COPY_NODE_FIELD(fromClause); COPY_NODE_FIELD(fromClause);
COPY_NODE_FIELD(returningList);
return newnode; return newnode;
} }
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.280 2006/08/10 02:36:28 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.281 2006/08/12 02:52:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -664,6 +664,7 @@ _equalQuery(Query *a, Query *b) ...@@ -664,6 +664,7 @@ _equalQuery(Query *a, Query *b)
COMPARE_NODE_FIELD(rtable); COMPARE_NODE_FIELD(rtable);
COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(jointree);
COMPARE_NODE_FIELD(targetList); COMPARE_NODE_FIELD(targetList);
COMPARE_NODE_FIELD(returningList);
COMPARE_NODE_FIELD(groupClause); COMPARE_NODE_FIELD(groupClause);
COMPARE_NODE_FIELD(havingQual); COMPARE_NODE_FIELD(havingQual);
COMPARE_NODE_FIELD(distinctClause); COMPARE_NODE_FIELD(distinctClause);
...@@ -673,6 +674,7 @@ _equalQuery(Query *a, Query *b) ...@@ -673,6 +674,7 @@ _equalQuery(Query *a, Query *b)
COMPARE_NODE_FIELD(rowMarks); COMPARE_NODE_FIELD(rowMarks);
COMPARE_NODE_FIELD(setOperations); COMPARE_NODE_FIELD(setOperations);
COMPARE_NODE_FIELD(resultRelations); COMPARE_NODE_FIELD(resultRelations);
COMPARE_NODE_FIELD(returningLists);
return true; return true;
} }
...@@ -683,6 +685,7 @@ _equalInsertStmt(InsertStmt *a, InsertStmt *b) ...@@ -683,6 +685,7 @@ _equalInsertStmt(InsertStmt *a, InsertStmt *b)
COMPARE_NODE_FIELD(relation); COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(cols); COMPARE_NODE_FIELD(cols);
COMPARE_NODE_FIELD(selectStmt); COMPARE_NODE_FIELD(selectStmt);
COMPARE_NODE_FIELD(returningList);
return true; return true;
} }
...@@ -691,8 +694,9 @@ static bool ...@@ -691,8 +694,9 @@ static bool
_equalDeleteStmt(DeleteStmt *a, DeleteStmt *b) _equalDeleteStmt(DeleteStmt *a, DeleteStmt *b)
{ {
COMPARE_NODE_FIELD(relation); COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(whereClause);
COMPARE_NODE_FIELD(usingClause); COMPARE_NODE_FIELD(usingClause);
COMPARE_NODE_FIELD(whereClause);
COMPARE_NODE_FIELD(returningList);
return true; return true;
} }
...@@ -704,6 +708,7 @@ _equalUpdateStmt(UpdateStmt *a, UpdateStmt *b) ...@@ -704,6 +708,7 @@ _equalUpdateStmt(UpdateStmt *a, UpdateStmt *b)
COMPARE_NODE_FIELD(targetList); COMPARE_NODE_FIELD(targetList);
COMPARE_NODE_FIELD(whereClause); COMPARE_NODE_FIELD(whereClause);
COMPARE_NODE_FIELD(fromClause); COMPARE_NODE_FIELD(fromClause);
COMPARE_NODE_FIELD(returningList);
return true; return true;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.281 2006/08/10 02:36:28 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.282 2006/08/12 02:52:04 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -1525,6 +1525,7 @@ _outQuery(StringInfo str, Query *node) ...@@ -1525,6 +1525,7 @@ _outQuery(StringInfo str, Query *node)
WRITE_NODE_FIELD(rtable); WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(jointree);
WRITE_NODE_FIELD(targetList); WRITE_NODE_FIELD(targetList);
WRITE_NODE_FIELD(returningList);
WRITE_NODE_FIELD(groupClause); WRITE_NODE_FIELD(groupClause);
WRITE_NODE_FIELD(havingQual); WRITE_NODE_FIELD(havingQual);
WRITE_NODE_FIELD(distinctClause); WRITE_NODE_FIELD(distinctClause);
...@@ -1534,6 +1535,7 @@ _outQuery(StringInfo str, Query *node) ...@@ -1534,6 +1535,7 @@ _outQuery(StringInfo str, Query *node)
WRITE_NODE_FIELD(rowMarks); WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(setOperations); WRITE_NODE_FIELD(setOperations);
WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(returningLists);
} }
static void static void
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.194 2006/08/10 02:36:28 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.195 2006/08/12 02:52:04 tgl Exp $
* *
* NOTES * NOTES
* Path and Plan nodes do not have any readfuncs support, because we * Path and Plan nodes do not have any readfuncs support, because we
...@@ -148,6 +148,7 @@ _readQuery(void) ...@@ -148,6 +148,7 @@ _readQuery(void)
READ_NODE_FIELD(rtable); READ_NODE_FIELD(rtable);
READ_NODE_FIELD(jointree); READ_NODE_FIELD(jointree);
READ_NODE_FIELD(targetList); READ_NODE_FIELD(targetList);
READ_NODE_FIELD(returningList);
READ_NODE_FIELD(groupClause); READ_NODE_FIELD(groupClause);
READ_NODE_FIELD(havingQual); READ_NODE_FIELD(havingQual);
READ_NODE_FIELD(distinctClause); READ_NODE_FIELD(distinctClause);
...@@ -157,6 +158,7 @@ _readQuery(void) ...@@ -157,6 +158,7 @@ _readQuery(void)
READ_NODE_FIELD(rowMarks); READ_NODE_FIELD(rowMarks);
READ_NODE_FIELD(setOperations); READ_NODE_FIELD(setOperations);
READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(returningLists);
READ_DONE(); READ_DONE();
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.20 2006/07/27 19:52:05 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.21 2006/08/12 02:52:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -439,6 +439,7 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info) ...@@ -439,6 +439,7 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
subparse->commandType = CMD_SELECT; subparse->commandType = CMD_SELECT;
subparse->resultRelation = 0; subparse->resultRelation = 0;
subparse->resultRelations = NIL; subparse->resultRelations = NIL;
subparse->returningLists = NIL;
subparse->into = NULL; subparse->into = NULL;
subparse->hasAggs = false; subparse->hasAggs = false;
subparse->groupClause = NIL; subparse->groupClause = NIL;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.207 2006/08/05 17:21:52 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.208 2006/08/12 02:52:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -280,6 +280,10 @@ subquery_planner(Query *parse, double tuple_fraction, ...@@ -280,6 +280,10 @@ subquery_planner(Query *parse, double tuple_fraction,
preprocess_expression(root, (Node *) parse->targetList, preprocess_expression(root, (Node *) parse->targetList,
EXPRKIND_TARGET); EXPRKIND_TARGET);
parse->returningList = (List *)
preprocess_expression(root, (Node *) parse->returningList,
EXPRKIND_TARGET);
preprocess_qual_conditions(root, (Node *) parse->jointree); preprocess_qual_conditions(root, (Node *) parse->jointree);
parse->havingQual = preprocess_expression(root, parse->havingQual, parse->havingQual = preprocess_expression(root, parse->havingQual,
...@@ -554,13 +558,13 @@ inheritance_planner(PlannerInfo *root) ...@@ -554,13 +558,13 @@ inheritance_planner(PlannerInfo *root)
Query *parse = root->parse; Query *parse = root->parse;
int parentRTindex = parse->resultRelation; int parentRTindex = parse->resultRelation;
List *subplans = NIL; List *subplans = NIL;
List *resultRelations = NIL;
List *returningLists = NIL;
List *rtable = NIL; List *rtable = NIL;
List *tlist = NIL; List *tlist = NIL;
PlannerInfo subroot; PlannerInfo subroot;
ListCell *l; ListCell *l;
parse->resultRelations = NIL;
foreach(l, root->append_rel_list) foreach(l, root->append_rel_list)
{ {
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
...@@ -605,10 +609,20 @@ inheritance_planner(PlannerInfo *root) ...@@ -605,10 +609,20 @@ inheritance_planner(PlannerInfo *root)
subplans = lappend(subplans, subplan); subplans = lappend(subplans, subplan);
/* Build target-relations list for the executor */ /* Build target-relations list for the executor */
parse->resultRelations = lappend_int(parse->resultRelations, resultRelations = lappend_int(resultRelations, appinfo->child_relid);
appinfo->child_relid);
/* Build list of per-relation RETURNING targetlists */
if (parse->returningList)
{
Assert(list_length(subroot.parse->returningLists) == 1);
returningLists = list_concat(returningLists,
subroot.parse->returningLists);
}
} }
parse->resultRelations = resultRelations;
parse->returningLists = returningLists;
/* Mark result as unordered (probably unnecessary) */ /* Mark result as unordered (probably unnecessary) */
root->query_pathkeys = NIL; root->query_pathkeys = NIL;
...@@ -1082,6 +1096,21 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) ...@@ -1082,6 +1096,21 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
count_est); count_est);
} }
/*
* Deal with the RETURNING clause if any. It's convenient to pass the
* returningList through setrefs.c now rather than at top level (if
* we waited, handling inherited UPDATE/DELETE would be much harder).
*/
if (parse->returningList)
{
List *rlist;
rlist = set_returning_clause_references(parse->returningList,
result_plan,
parse->resultRelation);
parse->returningLists = list_make1(rlist);
}
/* /*
* Return the actual output ordering in query_pathkeys for possible use by * Return the actual output ordering in query_pathkeys for possible use by
* an outer query level. * an outer query level.
......
This diff is collapsed.
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.40 2006/08/10 02:36:28 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.41 2006/08/12 02:52:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -372,6 +372,10 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, ...@@ -372,6 +372,10 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
ResolveNew((Node *) parse->targetList, ResolveNew((Node *) parse->targetList,
varno, 0, rte, varno, 0, rte,
subtlist, CMD_SELECT, 0); subtlist, CMD_SELECT, 0);
parse->returningList = (List *)
ResolveNew((Node *) parse->returningList,
varno, 0, rte,
subtlist, CMD_SELECT, 0);
resolvenew_in_jointree((Node *) parse->jointree, varno, resolvenew_in_jointree((Node *) parse->jointree, varno,
rte, subtlist); rte, subtlist);
Assert(parse->setOperations == NULL); Assert(parse->setOperations == NULL);
......
...@@ -9,13 +9,14 @@ ...@@ -9,13 +9,14 @@
* relation in the correct order. For both UPDATE and DELETE queries, * relation in the correct order. For both UPDATE and DELETE queries,
* we need a junk targetlist entry holding the CTID attribute --- the * we need a junk targetlist entry holding the CTID attribute --- the
* executor relies on this to find the tuple to be replaced/deleted. * executor relies on this to find the tuple to be replaced/deleted.
* We may also need junk tlist entries for Vars used in the RETURNING list.
* *
* *
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.82 2006/04/30 18:30:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.83 2006/08/12 02:52:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -27,6 +28,8 @@ ...@@ -27,6 +28,8 @@
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "optimizer/prep.h" #include "optimizer/prep.h"
#include "optimizer/subselect.h" #include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/analyze.h" #include "parser/analyze.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
...@@ -151,6 +154,40 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) ...@@ -151,6 +154,40 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
} }
} }
/*
* If the query has a RETURNING list, add resjunk entries for any Vars
* used in RETURNING that belong to other relations. We need to do this
* to make these Vars available for the RETURNING calculation. Vars
* that belong to the result rel don't need to be added, because they
* will be made to refer to the actual heap tuple.
*/
if (parse->returningList && list_length(parse->rtable) > 1)
{
List *vars;
ListCell *l;
vars = pull_var_clause((Node *) parse->returningList, false);
foreach(l, vars)
{
Var *var = (Var *) lfirst(l);
TargetEntry *tle;
if (var->varno == result_relation)
continue; /* don't need it */
if (tlist_member((Node *) var, tlist))
continue; /* already got it */
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
NULL,
true);
tlist = lappend(tlist, tle);
}
list_free(vars);
}
return tlist; return tlist;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.217 2006/08/04 14:09:51 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.218 2006/08/12 02:52:05 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -3351,6 +3351,8 @@ query_tree_walker(Query *query, ...@@ -3351,6 +3351,8 @@ query_tree_walker(Query *query,
if (walker((Node *) query->targetList, context)) if (walker((Node *) query->targetList, context))
return true; return true;
if (walker((Node *) query->returningList, context))
return true;
if (walker((Node *) query->jointree, context)) if (walker((Node *) query->jointree, context))
return true; return true;
if (walker(query->setOperations, context)) if (walker(query->setOperations, context))
...@@ -3913,6 +3915,7 @@ query_tree_mutator(Query *query, ...@@ -3913,6 +3915,7 @@ query_tree_mutator(Query *query,
} }
MUTATE(query->targetList, query->targetList, List *); MUTATE(query->targetList, query->targetList, List *);
MUTATE(query->returningList, query->returningList, List *);
MUTATE(query->jointree, query->jointree, FromExpr *); MUTATE(query->jointree, query->jointree, FromExpr *);
MUTATE(query->setOperations, query->setOperations, Node *); MUTATE(query->setOperations, query->setOperations, Node *);
MUTATE(query->havingQual, query->havingQual, Node *); MUTATE(query->havingQual, query->havingQual, Node *);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.344 2006/08/10 02:36:29 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.345 2006/08/12 02:52:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -100,6 +100,7 @@ static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt, ...@@ -100,6 +100,7 @@ static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
List **extras_before, List **extras_after); List **extras_before, List **extras_after);
static List *transformInsertRow(ParseState *pstate, List *exprlist, static List *transformInsertRow(ParseState *pstate, List *exprlist,
List *stmtcols, List *icolumns, List *attrnos); List *stmtcols, List *icolumns, List *attrnos);
static List *transformReturningList(ParseState *pstate, List *returningList);
static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt); static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt, static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
List **extras_before, List **extras_after); List **extras_before, List **extras_after);
...@@ -494,9 +495,10 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) ...@@ -494,9 +495,10 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
*/ */
transformFromClause(pstate, stmt->usingClause); transformFromClause(pstate, stmt->usingClause);
/* fix where clause */
qual = transformWhereClause(pstate, stmt->whereClause, "WHERE"); qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
qry->returningList = transformReturningList(pstate, stmt->returningList);
/* done building the range table and jointree */ /* done building the range table and jointree */
qry->rtable = pstate->p_rtable; qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
...@@ -820,6 +822,22 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, ...@@ -820,6 +822,22 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
attnos = lnext(attnos); attnos = lnext(attnos);
} }
/*
* If we have a RETURNING clause, we need to add the target relation
* to the query namespace before processing it, so that Var references
* in RETURNING will work. Also, remove any namespace entries added
* in a sub-SELECT or VALUES list.
*/
if (stmt->returningList)
{
pstate->p_relnamespace = NIL;
pstate->p_varnamespace = NIL;
addRTEtoQuery(pstate, pstate->p_target_rangetblentry,
false, true, true);
qry->returningList = transformReturningList(pstate,
stmt->returningList);
}
/* done building the range table and jointree */ /* done building the range table and jointree */
qry->rtable = pstate->p_rtable; qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
...@@ -1297,7 +1315,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, ...@@ -1297,7 +1315,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
if (including_indexes) if (including_indexes)
elog(ERROR, "TODO"); elog(ERROR, "TODO");
/* /*
* Insert the inherited attributes into the cxt for the new table * Insert the inherited attributes into the cxt for the new table
* definition. * definition.
...@@ -1368,11 +1386,11 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, ...@@ -1368,11 +1386,11 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
def->cooked_default = pstrdup(this_default); def->cooked_default = pstrdup(this_default);
} }
} }
if (including_constraints && tupleDesc->constr) { if (including_constraints && tupleDesc->constr) {
int ccnum; int ccnum;
AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns); AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
for(ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++) { for(ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++) {
char *ccname = tupleDesc->constr->check[ccnum].ccname; char *ccname = tupleDesc->constr->check[ccnum].ccname;
char *ccbin = tupleDesc->constr->check[ccnum].ccbin; char *ccbin = tupleDesc->constr->check[ccnum].ccbin;
...@@ -1380,7 +1398,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, ...@@ -1380,7 +1398,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
Constraint *n = makeNode(Constraint); Constraint *n = makeNode(Constraint);
change_varattnos_of_a_node(ccbin_node, attmap); change_varattnos_of_a_node(ccbin_node, attmap);
n->contype = CONSTR_CHECK; n->contype = CONSTR_CHECK;
n->name = pstrdup(ccname); n->name = pstrdup(ccname);
n->raw_expr = ccbin_node; n->raw_expr = ccbin_node;
...@@ -2777,6 +2795,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) ...@@ -2777,6 +2795,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
qual = transformWhereClause(pstate, stmt->whereClause, "WHERE"); qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
qry->returningList = transformReturningList(pstate, stmt->returningList);
qry->rtable = pstate->p_rtable; qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
...@@ -2851,7 +2871,62 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) ...@@ -2851,7 +2871,62 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
} }
/* /*
* tranformAlterTableStmt - * transformReturningList -
* handle a RETURNING clause in INSERT/UPDATE/DELETE
*/
static List *
transformReturningList(ParseState *pstate, List *returningList)
{
List *rlist;
int save_next_resno;
bool save_hasAggs;
int length_rtable;
if (returningList == NIL)
return NIL; /* nothing to do */
/*
* We need to assign resnos starting at one in the RETURNING list.
* Save and restore the main tlist's value of p_next_resno, just in
* case someone looks at it later (probably won't happen).
*/
save_next_resno = pstate->p_next_resno;
pstate->p_next_resno = 1;
/* save other state so that we can detect disallowed stuff */
save_hasAggs = pstate->p_hasAggs;
pstate->p_hasAggs = false;
length_rtable = list_length(pstate->p_rtable);
/* transform RETURNING identically to a SELECT targetlist */
rlist = transformTargetList(pstate, returningList);
/* check for disallowed stuff */
/* aggregates not allowed (but subselects are okay) */
if (pstate->p_hasAggs)
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in RETURNING")));
/* no new relation references please */
if (list_length(pstate->p_rtable) != length_rtable)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("RETURNING may not contain references to other relations")));
/* mark column origins */
markTargetListOrigins(pstate, rlist);
/* restore state */
pstate->p_next_resno = save_next_resno;
pstate->p_hasAggs = save_hasAggs;
return rlist;
}
/*
* transformAlterTableStmt -
* transform an Alter Table Statement * transform an Alter Table Statement
*/ */
static Query * static Query *
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.554 2006/08/02 01:59:46 joe Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.555 2006/08/12 02:52:05 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -244,7 +244,7 @@ static void doNegateFloat(Value *v); ...@@ -244,7 +244,7 @@ static void doNegateFloat(Value *v);
transaction_mode_list_or_empty transaction_mode_list_or_empty
TableFuncElementList TableFuncElementList
prep_type_clause prep_type_list prep_type_clause prep_type_list
execute_param_clause using_clause execute_param_clause using_clause returning_clause
%type <range> into_clause OptTempTableName %type <range> into_clause OptTempTableName
...@@ -412,7 +412,7 @@ static void doNegateFloat(Value *v); ...@@ -412,7 +412,7 @@ static void doNegateFloat(Value *v);
QUOTE QUOTE
READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
REPEATABLE REPLACE RESET RESTART RESTRICT RETURNS REVOKE RIGHT REPEATABLE REPLACE RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT
ROLE ROLLBACK ROW ROWS RULE ROLE ROLLBACK ROW ROWS RULE
SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
...@@ -5334,9 +5334,10 @@ DeallocateStmt: DEALLOCATE name ...@@ -5334,9 +5334,10 @@ DeallocateStmt: DEALLOCATE name
*****************************************************************************/ *****************************************************************************/
InsertStmt: InsertStmt:
INSERT INTO qualified_name insert_rest INSERT INTO qualified_name insert_rest returning_clause
{ {
$4->relation = $3; $4->relation = $3;
$4->returningList = $5;
$$ = (Node *) $4; $$ = (Node *) $4;
} }
; ;
...@@ -5380,6 +5381,11 @@ insert_column_item: ...@@ -5380,6 +5381,11 @@ insert_column_item:
} }
; ;
returning_clause:
RETURNING target_list { $$ = $2; }
| /* EMPTY */ { $$ = NIL; }
;
/***************************************************************************** /*****************************************************************************
* *
...@@ -5389,12 +5395,13 @@ insert_column_item: ...@@ -5389,12 +5395,13 @@ insert_column_item:
*****************************************************************************/ *****************************************************************************/
DeleteStmt: DELETE_P FROM relation_expr_opt_alias DeleteStmt: DELETE_P FROM relation_expr_opt_alias
using_clause where_clause using_clause where_clause returning_clause
{ {
DeleteStmt *n = makeNode(DeleteStmt); DeleteStmt *n = makeNode(DeleteStmt);
n->relation = $3; n->relation = $3;
n->usingClause = $4; n->usingClause = $4;
n->whereClause = $5; n->whereClause = $5;
n->returningList = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
...@@ -5445,12 +5452,14 @@ UpdateStmt: UPDATE relation_expr_opt_alias ...@@ -5445,12 +5452,14 @@ UpdateStmt: UPDATE relation_expr_opt_alias
SET update_target_list SET update_target_list
from_clause from_clause
where_clause where_clause
returning_clause
{ {
UpdateStmt *n = makeNode(UpdateStmt); UpdateStmt *n = makeNode(UpdateStmt);
n->relation = $2; n->relation = $2;
n->targetList = $4; n->targetList = $4;
n->fromClause = $5; n->fromClause = $5;
n->whereClause = $6; n->whereClause = $6;
n->returningList = $7;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
...@@ -8809,6 +8818,7 @@ reserved_keyword: ...@@ -8809,6 +8818,7 @@ reserved_keyword:
| PLACING | PLACING
| PRIMARY | PRIMARY
| REFERENCES | REFERENCES
| RETURNING
| SELECT | SELECT
| SESSION_USER | SESSION_USER
| SOME | SOME
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.174 2006/07/31 01:16:37 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.175 2006/08/12 02:52:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -284,6 +284,7 @@ static const ScanKeyword ScanKeywords[] = { ...@@ -284,6 +284,7 @@ static const ScanKeyword ScanKeywords[] = {
{"reset", RESET}, {"reset", RESET},
{"restart", RESTART}, {"restart", RESTART},
{"restrict", RESTRICT}, {"restrict", RESTRICT},
{"returning", RETURNING},
{"returns", RETURNS}, {"returns", RETURNS},
{"revoke", REVOKE}, {"revoke", REVOKE},
{"right", RIGHT}, {"right", RIGHT},
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/dest.c,v 1.68 2006/03/05 15:58:40 momjian Exp $ * $PostgreSQL: pgsql/src/backend/tcop/dest.c,v 1.69 2006/08/12 02:52:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "access/printtup.h" #include "access/printtup.h"
#include "access/xact.h" #include "access/xact.h"
#include "executor/executor.h"
#include "executor/tstoreReceiver.h" #include "executor/tstoreReceiver.h"
#include "libpq/libpq.h" #include "libpq/libpq.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
...@@ -124,6 +125,9 @@ CreateDestReceiver(CommandDest dest, Portal portal) ...@@ -124,6 +125,9 @@ CreateDestReceiver(CommandDest dest, Portal portal)
elog(ERROR, "portal has no holdStore"); elog(ERROR, "portal has no holdStore");
return CreateTuplestoreDestReceiver(portal->holdStore, return CreateTuplestoreDestReceiver(portal->holdStore,
portal->holdContext); portal->holdContext);
case DestIntoRel:
return CreateIntoRelDestReceiver();
} }
/* should never get here */ /* should never get here */
...@@ -148,6 +152,7 @@ EndCommand(const char *commandTag, CommandDest dest) ...@@ -148,6 +152,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case DestDebug: case DestDebug:
case DestSPI: case DestSPI:
case DestTuplestore: case DestTuplestore:
case DestIntoRel:
break; break;
} }
} }
...@@ -186,6 +191,7 @@ NullCommand(CommandDest dest) ...@@ -186,6 +191,7 @@ NullCommand(CommandDest dest)
case DestDebug: case DestDebug:
case DestSPI: case DestSPI:
case DestTuplestore: case DestTuplestore:
case DestIntoRel:
break; break;
} }
} }
...@@ -226,6 +232,7 @@ ReadyForQuery(CommandDest dest) ...@@ -226,6 +232,7 @@ ReadyForQuery(CommandDest dest)
case DestDebug: case DestDebug:
case DestSPI: case DestSPI:
case DestTuplestore: case DestTuplestore:
case DestIntoRel:
break; break;
} }
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.105 2006/07/14 14:52:23 momjian Exp $ * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.106 2006/08/12 02:52:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -37,6 +37,7 @@ static void ProcessQuery(Query *parsetree, ...@@ -37,6 +37,7 @@ static void ProcessQuery(Query *parsetree,
ParamListInfo params, ParamListInfo params,
DestReceiver *dest, DestReceiver *dest,
char *completionTag); char *completionTag);
static void FillPortalStore(Portal portal);
static uint32 RunFromStore(Portal portal, ScanDirection direction, long count, static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
DestReceiver *dest); DestReceiver *dest);
static long PortalRunSelect(Portal portal, bool forward, long count, static long PortalRunSelect(Portal portal, bool forward, long count,
...@@ -99,7 +100,8 @@ FreeQueryDesc(QueryDesc *qdesc) ...@@ -99,7 +100,8 @@ FreeQueryDesc(QueryDesc *qdesc)
/* /*
* ProcessQuery * ProcessQuery
* Execute a single plannable query within a PORTAL_MULTI_QUERY portal * Execute a single plannable query within a PORTAL_MULTI_QUERY
* or PORTAL_ONE_RETURNING portal
* *
* parsetree: the query tree * parsetree: the query tree
* plan: the plan tree for the query * plan: the plan tree for the query
...@@ -126,24 +128,6 @@ ProcessQuery(Query *parsetree, ...@@ -126,24 +128,6 @@ ProcessQuery(Query *parsetree,
ereport(DEBUG3, ereport(DEBUG3,
(errmsg_internal("ProcessQuery"))); (errmsg_internal("ProcessQuery")));
/*
* Check for special-case destinations
*/
if (operation == CMD_SELECT)
{
if (parsetree->into != NULL)
{
/*
* SELECT INTO table (a/k/a CREATE AS ... SELECT).
*
* Override the normal communication destination; execMain.c
* special-cases this case. (Perhaps would be cleaner to have an
* additional destination type?)
*/
dest = None_Receiver;
}
}
/* /*
* Must always set snapshot for plannable queries. Note we assume that * Must always set snapshot for plannable queries. Note we assume that
* caller will take care of restoring ActiveSnapshot on exit/error. * caller will take care of restoring ActiveSnapshot on exit/error.
...@@ -237,16 +221,19 @@ ChoosePortalStrategy(List *parseTrees) ...@@ -237,16 +221,19 @@ ChoosePortalStrategy(List *parseTrees)
{ {
Query *query = (Query *) linitial(parseTrees); Query *query = (Query *) linitial(parseTrees);
if (query->commandType == CMD_SELECT && if (query->canSetTag)
query->canSetTag &&
query->into == NULL)
strategy = PORTAL_ONE_SELECT;
else if (query->commandType == CMD_UTILITY &&
query->canSetTag &&
query->utilityStmt != NULL)
{ {
if (UtilityReturnsTuples(query->utilityStmt)) if (query->commandType == CMD_SELECT &&
strategy = PORTAL_UTIL_SELECT; query->into == NULL)
strategy = PORTAL_ONE_SELECT;
else if (query->returningList != NIL)
strategy = PORTAL_ONE_RETURNING;
else if (query->commandType == CMD_UTILITY &&
query->utilityStmt != NULL)
{
if (UtilityReturnsTuples(query->utilityStmt))
strategy = PORTAL_UTIL_SELECT;
}
} }
} }
return strategy; return strategy;
...@@ -267,6 +254,8 @@ FetchPortalTargetList(Portal portal) ...@@ -267,6 +254,8 @@ FetchPortalTargetList(Portal portal)
{ {
if (portal->strategy == PORTAL_ONE_SELECT) if (portal->strategy == PORTAL_ONE_SELECT)
return ((Query *) linitial(portal->parseTrees))->targetList; return ((Query *) linitial(portal->parseTrees))->targetList;
if (portal->strategy == PORTAL_ONE_RETURNING)
return ((Query *) linitial(portal->parseTrees))->returningList;
if (portal->strategy == PORTAL_UTIL_SELECT) if (portal->strategy == PORTAL_UTIL_SELECT)
{ {
Node *utilityStmt; Node *utilityStmt;
...@@ -426,6 +415,24 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) ...@@ -426,6 +415,24 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
portal->posOverflow = false; portal->posOverflow = false;
break; break;
case PORTAL_ONE_RETURNING:
/*
* We don't start the executor until we are told to run
* the portal. We do need to set up the result tupdesc.
*/
portal->tupDesc =
ExecCleanTypeFromTL(((Query *) linitial(portal->parseTrees))->returningList, false);
/*
* Reset cursor position data to "start of query"
*/
portal->atStart = true;
portal->atEnd = false; /* allow fetches */
portal->portalPos = 0;
portal->posOverflow = false;
break;
case PORTAL_UTIL_SELECT: case PORTAL_UTIL_SELECT:
/* /*
...@@ -618,6 +625,7 @@ PortalRun(Portal portal, long count, ...@@ -618,6 +625,7 @@ PortalRun(Portal portal, long count,
{ {
case PORTAL_ONE_SELECT: case PORTAL_ONE_SELECT:
(void) PortalRunSelect(portal, true, count, dest); (void) PortalRunSelect(portal, true, count, dest);
/* we know the query is supposed to set the tag */ /* we know the query is supposed to set the tag */
if (completionTag && portal->commandTag) if (completionTag && portal->commandTag)
strcpy(completionTag, portal->commandTag); strcpy(completionTag, portal->commandTag);
...@@ -631,33 +639,22 @@ PortalRun(Portal portal, long count, ...@@ -631,33 +639,22 @@ PortalRun(Portal portal, long count,
result = portal->atEnd; result = portal->atEnd;
break; break;
case PORTAL_ONE_RETURNING:
case PORTAL_UTIL_SELECT: case PORTAL_UTIL_SELECT:
/* /*
* If we have not yet run the utility statement, do so, * If we have not yet run the command, do so,
* storing its results in the portal's tuplestore. * storing its results in the portal's tuplestore.
*/ */
if (!portal->portalUtilReady) if (!portal->holdStore)
{ FillPortalStore(portal);
DestReceiver *treceiver;
PortalCreateHoldStore(portal);
treceiver = CreateDestReceiver(DestTuplestore, portal);
PortalRunUtility(portal, linitial(portal->parseTrees),
treceiver, NULL);
(*treceiver->rDestroy) (treceiver);
portal->portalUtilReady = true;
}
/* /*
* Now fetch desired portion of results. * Now fetch desired portion of results.
*/ */
(void) PortalRunSelect(portal, true, count, dest); (void) PortalRunSelect(portal, true, count, dest);
/* /* we know the query is supposed to set the tag */
* We know the query is supposed to set the tag; we assume
* only the default tag is needed.
*/
if (completionTag && portal->commandTag) if (completionTag && portal->commandTag)
strcpy(completionTag, portal->commandTag); strcpy(completionTag, portal->commandTag);
...@@ -731,7 +728,9 @@ PortalRun(Portal portal, long count, ...@@ -731,7 +728,9 @@ PortalRun(Portal portal, long count,
/* /*
* PortalRunSelect * PortalRunSelect
* Execute a portal's query in SELECT cases (also UTIL_SELECT). * Execute a portal's query in PORTAL_ONE_SELECT mode, and also
* when fetching from a completed holdStore in PORTAL_ONE_RETURNING
* and PORTAL_UTIL_SELECT cases.
* *
* This handles simple N-rows-forward-or-backward cases. For more complex * This handles simple N-rows-forward-or-backward cases. For more complex
* nonsequential access to a portal, see PortalRunFetch. * nonsequential access to a portal, see PortalRunFetch.
...@@ -876,6 +875,47 @@ PortalRunSelect(Portal portal, ...@@ -876,6 +875,47 @@ PortalRunSelect(Portal portal,
return nprocessed; return nprocessed;
} }
/*
* FillPortalStore
* Run the query and load result tuples into the portal's tuple store.
*
* This is used for PORTAL_ONE_RETURNING and PORTAL_UTIL_SELECT cases only.
*/
static void
FillPortalStore(Portal portal)
{
DestReceiver *treceiver;
char completionTag[COMPLETION_TAG_BUFSIZE];
PortalCreateHoldStore(portal);
treceiver = CreateDestReceiver(DestTuplestore, portal);
switch (portal->strategy)
{
case PORTAL_ONE_RETURNING:
/*
* We run the query just as if it were in a MULTI portal,
* but send the output to the tuplestore.
*/
PortalRunMulti(portal, treceiver, treceiver, completionTag);
/* Override default completion tag with actual command result */
portal->commandTag = pstrdup(completionTag);
break;
case PORTAL_UTIL_SELECT:
PortalRunUtility(portal, linitial(portal->parseTrees),
treceiver, NULL);
break;
default:
elog(ERROR, "unsupported portal strategy: %d",
(int) portal->strategy);
break;
}
(*treceiver->rDestroy) (treceiver);
}
/* /*
* RunFromStore * RunFromStore
* Fetch tuples from the portal's tuple store. * Fetch tuples from the portal's tuple store.
...@@ -1009,7 +1049,8 @@ PortalRunUtility(Portal portal, Query *query, ...@@ -1009,7 +1049,8 @@ PortalRunUtility(Portal portal, Query *query,
/* /*
* PortalRunMulti * PortalRunMulti
* Execute a portal's queries in the general case (multi queries). * Execute a portal's queries in the general case (multi queries
* or non-SELECT-like queries)
*/ */
static void static void
PortalRunMulti(Portal portal, PortalRunMulti(Portal portal,
...@@ -1178,23 +1219,15 @@ PortalRunFetch(Portal portal, ...@@ -1178,23 +1219,15 @@ PortalRunFetch(Portal portal,
result = DoPortalRunFetch(portal, fdirection, count, dest); result = DoPortalRunFetch(portal, fdirection, count, dest);
break; break;
case PORTAL_ONE_RETURNING:
case PORTAL_UTIL_SELECT: case PORTAL_UTIL_SELECT:
/* /*
* If we have not yet run the utility statement, do so, * If we have not yet run the command, do so,
* storing its results in the portal's tuplestore. * storing its results in the portal's tuplestore.
*/ */
if (!portal->portalUtilReady) if (!portal->holdStore)
{ FillPortalStore(portal);
DestReceiver *treceiver;
PortalCreateHoldStore(portal);
treceiver = CreateDestReceiver(DestTuplestore, portal);
PortalRunUtility(portal, linitial(portal->parseTrees),
treceiver, NULL);
(*treceiver->rDestroy) (treceiver);
portal->portalUtilReady = true;
}
/* /*
* Now fetch desired portion of results. * Now fetch desired portion of results.
...@@ -1253,6 +1286,7 @@ DoPortalRunFetch(Portal portal, ...@@ -1253,6 +1286,7 @@ DoPortalRunFetch(Portal portal,
bool forward; bool forward;
Assert(portal->strategy == PORTAL_ONE_SELECT || Assert(portal->strategy == PORTAL_ONE_SELECT ||
portal->strategy == PORTAL_ONE_RETURNING ||
portal->strategy == PORTAL_UTIL_SELECT); portal->strategy == PORTAL_UTIL_SELECT);
switch (fdirection) switch (fdirection)
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.263 2006/07/31 01:16:37 tgl Exp $ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.264 2006/08/12 02:52:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1148,7 +1148,7 @@ UtilityReturnsTuples(Node *parsetree) ...@@ -1148,7 +1148,7 @@ UtilityReturnsTuples(Node *parsetree)
switch (ChoosePortalStrategy(entry->query_list)) switch (ChoosePortalStrategy(entry->query_list))
{ {
case PORTAL_ONE_SELECT: case PORTAL_ONE_SELECT:
return true; case PORTAL_ONE_RETURNING:
case PORTAL_UTIL_SELECT: case PORTAL_UTIL_SELECT:
return true; return true;
case PORTAL_MULTI_QUERY: case PORTAL_MULTI_QUERY:
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* ruleutils.c - Functions to convert stored expressions/querytrees * ruleutils.c - Functions to convert stored expressions/querytrees
* back to source text * back to source text
* *
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.230 2006/08/02 01:59:47 joe Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.231 2006/08/12 02:52:05 tgl Exp $
**********************************************************************/ **********************************************************************/
#include "postgres.h" #include "postgres.h"
...@@ -140,6 +140,8 @@ static void get_delete_query_def(Query *query, deparse_context *context); ...@@ -140,6 +140,8 @@ static void get_delete_query_def(Query *query, deparse_context *context);
static void get_utility_query_def(Query *query, deparse_context *context); static void get_utility_query_def(Query *query, deparse_context *context);
static void get_basic_select_query(Query *query, deparse_context *context, static void get_basic_select_query(Query *query, deparse_context *context,
TupleDesc resultDesc); TupleDesc resultDesc);
static void get_target_list(List *targetList, deparse_context *context,
TupleDesc resultDesc);
static void get_setop_query(Node *setOp, Query *query, static void get_setop_query(Node *setOp, Query *query,
deparse_context *context, deparse_context *context,
TupleDesc resultDesc); TupleDesc resultDesc);
...@@ -1954,7 +1956,6 @@ get_basic_select_query(Query *query, deparse_context *context, ...@@ -1954,7 +1956,6 @@ get_basic_select_query(Query *query, deparse_context *context,
StringInfo buf = context->buf; StringInfo buf = context->buf;
char *sep; char *sep;
ListCell *l; ListCell *l;
int colno;
if (PRETTY_INDENT(context)) if (PRETTY_INDENT(context))
{ {
...@@ -2012,9 +2013,63 @@ get_basic_select_query(Query *query, deparse_context *context, ...@@ -2012,9 +2013,63 @@ get_basic_select_query(Query *query, deparse_context *context,
} }
/* Then we tell what to select (the targetlist) */ /* Then we tell what to select (the targetlist) */
get_target_list(query->targetList, context, resultDesc);
/* Add the FROM clause if needed */
get_from_clause(query, " FROM ", context);
/* Add the WHERE clause if given */
if (query->jointree->quals != NULL)
{
appendContextKeyword(context, " WHERE ",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_rule_expr(query->jointree->quals, context, false);
}
/* Add the GROUP BY clause if given */
if (query->groupClause != NULL)
{
appendContextKeyword(context, " GROUP BY ",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
sep = "";
foreach(l, query->groupClause)
{
GroupClause *grp = (GroupClause *) lfirst(l);
appendStringInfoString(buf, sep);
get_rule_sortgroupclause(grp, query->targetList,
false, context);
sep = ", ";
}
}
/* Add the HAVING clause if given */
if (query->havingQual != NULL)
{
appendContextKeyword(context, " HAVING ",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
get_rule_expr(query->havingQual, context, false);
}
}
/* ----------
* get_target_list - Parse back a SELECT target list
*
* This is also used for RETURNING lists in INSERT/UPDATE/DELETE.
* ----------
*/
static void
get_target_list(List *targetList, deparse_context *context,
TupleDesc resultDesc)
{
StringInfo buf = context->buf;
char *sep;
int colno;
ListCell *l;
sep = " "; sep = " ";
colno = 0; colno = 0;
foreach(l, query->targetList) foreach(l, targetList)
{ {
TargetEntry *tle = (TargetEntry *) lfirst(l); TargetEntry *tle = (TargetEntry *) lfirst(l);
char *colname; char *colname;
...@@ -2095,42 +2150,6 @@ get_basic_select_query(Query *query, deparse_context *context, ...@@ -2095,42 +2150,6 @@ get_basic_select_query(Query *query, deparse_context *context,
appendStringInfo(buf, " AS %s", quote_identifier(colname)); appendStringInfo(buf, " AS %s", quote_identifier(colname));
} }
} }
/* Add the FROM clause if needed */
get_from_clause(query, " FROM ", context);
/* Add the WHERE clause if given */
if (query->jointree->quals != NULL)
{
appendContextKeyword(context, " WHERE ",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_rule_expr(query->jointree->quals, context, false);
}
/* Add the GROUP BY clause if given */
if (query->groupClause != NULL)
{
appendContextKeyword(context, " GROUP BY ",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
sep = "";
foreach(l, query->groupClause)
{
GroupClause *grp = (GroupClause *) lfirst(l);
appendStringInfoString(buf, sep);
get_rule_sortgroupclause(grp, query->targetList,
false, context);
sep = ", ";
}
}
/* Add the HAVING clause if given */
if (query->havingQual != NULL)
{
appendContextKeyword(context, " HAVING ",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
get_rule_expr(query->havingQual, context, false);
}
} }
static void static void
...@@ -2377,6 +2396,14 @@ get_insert_query_def(Query *query, deparse_context *context) ...@@ -2377,6 +2396,14 @@ get_insert_query_def(Query *query, deparse_context *context)
get_rule_expr((Node *) strippedexprs, context, false); get_rule_expr((Node *) strippedexprs, context, false);
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
} }
/* Add RETURNING if present */
if (query->returningList)
{
appendContextKeyword(context, " RETURNING",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_target_list(query->returningList, context, NULL);
}
} }
...@@ -2441,13 +2468,21 @@ get_update_query_def(Query *query, deparse_context *context) ...@@ -2441,13 +2468,21 @@ get_update_query_def(Query *query, deparse_context *context)
/* Add the FROM clause if needed */ /* Add the FROM clause if needed */
get_from_clause(query, " FROM ", context); get_from_clause(query, " FROM ", context);
/* Finally add a WHERE clause if given */ /* Add a WHERE clause if given */
if (query->jointree->quals != NULL) if (query->jointree->quals != NULL)
{ {
appendContextKeyword(context, " WHERE ", appendContextKeyword(context, " WHERE ",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 1); -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_rule_expr(query->jointree->quals, context, false); get_rule_expr(query->jointree->quals, context, false);
} }
/* Add RETURNING if present */
if (query->returningList)
{
appendContextKeyword(context, " RETURNING",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_target_list(query->returningList, context, NULL);
}
} }
...@@ -2485,6 +2520,14 @@ get_delete_query_def(Query *query, deparse_context *context) ...@@ -2485,6 +2520,14 @@ get_delete_query_def(Query *query, deparse_context *context)
-PRETTYINDENT_STD, PRETTYINDENT_STD, 1); -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_rule_expr(query->jointree->quals, context, false); get_rule_expr(query->jointree->quals, context, false);
} }
/* Add RETURNING if present */
if (query->returningList)
{
appendContextKeyword(context, " RETURNING",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_target_list(query->returningList, context, NULL);
}
} }
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.348 2006/08/10 02:36:29 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.349 2006/08/12 02:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200608091 #define CATALOG_VERSION_NO 200608101
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.128 2006/08/04 21:33:36 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.129 2006/08/12 02:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -132,6 +132,7 @@ extern void ExecConstraints(ResultRelInfo *resultRelInfo, ...@@ -132,6 +132,7 @@ extern void ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate); TupleTableSlot *slot, EState *estate);
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
ItemPointer tid, TransactionId priorXmax, CommandId curCid); ItemPointer tid, TransactionId priorXmax, CommandId curCid);
extern DestReceiver *CreateIntoRelDestReceiver(void);
/* /*
* prototypes from functions in execProcnode.c * prototypes from functions in execProcnode.c
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.158 2006/08/04 21:33:36 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.159 2006/08/12 02:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -262,6 +262,7 @@ typedef struct JunkFilter ...@@ -262,6 +262,7 @@ typedef struct JunkFilter
* TrigInstrument optional runtime measurements for triggers * TrigInstrument optional runtime measurements for triggers
* ConstraintExprs array of constraint-checking expr states * ConstraintExprs array of constraint-checking expr states
* junkFilter for removing junk attributes from tuples * junkFilter for removing junk attributes from tuples
* projectReturning for computing a RETURNING list
* ---------------- * ----------------
*/ */
typedef struct ResultRelInfo typedef struct ResultRelInfo
...@@ -277,6 +278,7 @@ typedef struct ResultRelInfo ...@@ -277,6 +278,7 @@ typedef struct ResultRelInfo
struct Instrumentation *ri_TrigInstrument; struct Instrumentation *ri_TrigInstrument;
List **ri_ConstraintExprs; List **ri_ConstraintExprs;
JunkFilter *ri_junkFilter; JunkFilter *ri_junkFilter;
ProjectionInfo *ri_projectReturning;
} ResultRelInfo; } ResultRelInfo;
/* ---------------- /* ----------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.321 2006/08/10 02:36:29 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.322 2006/08/12 02:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -104,6 +104,8 @@ typedef struct Query ...@@ -104,6 +104,8 @@ typedef struct Query
List *targetList; /* target list (of TargetEntry) */ List *targetList; /* target list (of TargetEntry) */
List *returningList; /* return-values list (of TargetEntry) */
List *groupClause; /* a list of GroupClause's */ List *groupClause; /* a list of GroupClause's */
Node *havingQual; /* qualifications applied to groups */ Node *havingQual; /* qualifications applied to groups */
...@@ -125,10 +127,23 @@ typedef struct Query ...@@ -125,10 +127,23 @@ typedef struct Query
* tree, the planner will add all the child tables to the rtable and store * tree, the planner will add all the child tables to the rtable and store
* a list of the rtindexes of all the result relations here. This is done * a list of the rtindexes of all the result relations here. This is done
* at plan time, not parse time, since we don't want to commit to the * at plan time, not parse time, since we don't want to commit to the
* exact set of child tables at parse time. This field ought to go in * exact set of child tables at parse time. XXX This field ought to go in
* some sort of TopPlan plan node, not in the Query. * some sort of TopPlan plan node, not in the Query.
*/ */
List *resultRelations; /* integer list of RT indexes, or NIL */ List *resultRelations; /* integer list of RT indexes, or NIL */
/*
* If the query has a returningList then the planner will store a list
* of processed targetlists (one per result relation) here. We must
* have a separate RETURNING targetlist for each result rel because
* column numbers may vary within an inheritance tree. In the targetlists,
* Vars referencing the result relation will have their original varno
* and varattno, while Vars referencing other rels will be converted
* to have varno OUTER and varattno referencing a resjunk entry in the
* top plan node's targetlist. XXX This field ought to go in some sort of
* TopPlan plan node, not in the Query.
*/
List *returningLists; /* list of lists of TargetEntry, or NIL */
} Query; } Query;
...@@ -648,6 +663,7 @@ typedef struct InsertStmt ...@@ -648,6 +663,7 @@ typedef struct InsertStmt
RangeVar *relation; /* relation to insert into */ RangeVar *relation; /* relation to insert into */
List *cols; /* optional: names of the target columns */ List *cols; /* optional: names of the target columns */
Node *selectStmt; /* the source SELECT/VALUES, or NULL */ Node *selectStmt; /* the source SELECT/VALUES, or NULL */
List *returningList; /* list of expressions to return */
} InsertStmt; } InsertStmt;
/* ---------------------- /* ----------------------
...@@ -658,8 +674,9 @@ typedef struct DeleteStmt ...@@ -658,8 +674,9 @@ typedef struct DeleteStmt
{ {
NodeTag type; NodeTag type;
RangeVar *relation; /* relation to delete from */ RangeVar *relation; /* relation to delete from */
Node *whereClause; /* qualifications */
List *usingClause; /* optional using clause for more tables */ List *usingClause; /* optional using clause for more tables */
Node *whereClause; /* qualifications */
List *returningList; /* list of expressions to return */
} DeleteStmt; } DeleteStmt;
/* ---------------------- /* ----------------------
...@@ -673,6 +690,7 @@ typedef struct UpdateStmt ...@@ -673,6 +690,7 @@ typedef struct UpdateStmt
List *targetList; /* the target list (of ResTarget) */ List *targetList; /* the target list (of ResTarget) */
Node *whereClause; /* qualifications */ Node *whereClause; /* qualifications */
List *fromClause; /* optional from clause for more tables */ List *fromClause; /* optional from clause for more tables */
List *returningList; /* list of expressions to return */
} UpdateStmt; } UpdateStmt;
/* ---------------------- /* ----------------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.94 2006/07/26 00:34:48 momjian Exp $ * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.95 2006/08/12 02:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -80,6 +80,9 @@ extern void process_implied_equality(PlannerInfo *root, ...@@ -80,6 +80,9 @@ extern void process_implied_equality(PlannerInfo *root,
* prototypes for plan/setrefs.c * prototypes for plan/setrefs.c
*/ */
extern Plan *set_plan_references(Plan *plan, List *rtable); extern Plan *set_plan_references(Plan *plan, List *rtable);
extern List *set_returning_clause_references(List *rlist,
Plan *topplan,
Index resultRelation);
extern void fix_opfuncids(Node *node); extern void fix_opfuncids(Node *node);
extern void set_opfuncid(OpExpr *opexpr); extern void set_opfuncid(OpExpr *opexpr);
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
* dest.h * dest.h
* support for communication destinations * support for communication destinations
* *
* Whenever the backend executes a query, the results * Whenever the backend executes a query that returns tuples, the results
* have to go someplace. * have to go someplace. For example:
* *
* - stdout is the destination only when we are running a * - stdout is the destination only when we are running a
* standalone backend (no postmaster) and are returning results * standalone backend (no postmaster) and are returning results
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/tcop/dest.h,v 1.50 2006/03/05 15:59:00 momjian Exp $ * $PostgreSQL: pgsql/src/include/tcop/dest.h,v 1.51 2006/08/12 02:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -84,7 +84,8 @@ typedef enum ...@@ -84,7 +84,8 @@ typedef enum
DestRemote, /* results sent to frontend process */ DestRemote, /* results sent to frontend process */
DestRemoteExecute, /* sent to frontend, in Execute command */ DestRemoteExecute, /* sent to frontend, in Execute command */
DestSPI, /* results sent to SPI manager */ DestSPI, /* results sent to SPI manager */
DestTuplestore /* results sent to Tuplestore */ DestTuplestore, /* results sent to Tuplestore */
DestIntoRel /* results sent to relation (SELECT INTO) */
} CommandDest; } CommandDest;
/* ---------------- /* ----------------
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.64 2006/08/08 01:23:15 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.65 2006/08/12 02:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -62,6 +62,12 @@ ...@@ -62,6 +62,12 @@
* supports holdable cursors (the Executor results can be dumped into a * supports holdable cursors (the Executor results can be dumped into a
* tuplestore for access after transaction completion). * tuplestore for access after transaction completion).
* *
* PORTAL_ONE_RETURNING: the portal contains a single INSERT/UPDATE/DELETE
* query with a RETURNING clause. On first execution, we run the statement
* and dump its results into the portal tuplestore; the results are then
* returned to the client as demanded. (We can't support suspension of
* the query partway through, because the AFTER TRIGGER code can't cope.)
*
* PORTAL_UTIL_SELECT: the portal contains a utility statement that returns * PORTAL_UTIL_SELECT: the portal contains a utility statement that returns
* a SELECT-like result (for example, EXPLAIN or SHOW). On first execution, * a SELECT-like result (for example, EXPLAIN or SHOW). On first execution,
* we run the statement and dump its results into the portal tuplestore; * we run the statement and dump its results into the portal tuplestore;
...@@ -73,6 +79,7 @@ ...@@ -73,6 +79,7 @@
typedef enum PortalStrategy typedef enum PortalStrategy
{ {
PORTAL_ONE_SELECT, PORTAL_ONE_SELECT,
PORTAL_ONE_RETURNING,
PORTAL_UTIL_SELECT, PORTAL_UTIL_SELECT,
PORTAL_MULTI_QUERY PORTAL_MULTI_QUERY
} PortalStrategy; } PortalStrategy;
...@@ -133,7 +140,6 @@ typedef struct PortalData ...@@ -133,7 +140,6 @@ typedef struct PortalData
/* Status data */ /* Status data */
PortalStatus status; /* see above */ PortalStatus status; /* see above */
bool portalUtilReady; /* PortalRunUtility complete? */
/* If not NULL, Executor is active; call ExecutorEnd eventually: */ /* If not NULL, Executor is active; call ExecutorEnd eventually: */
QueryDesc *queryDesc; /* info needed for executor invocation */ QueryDesc *queryDesc; /* info needed for executor invocation */
...@@ -144,9 +150,9 @@ typedef struct PortalData ...@@ -144,9 +150,9 @@ typedef struct PortalData
int16 *formats; /* a format code for each column */ int16 *formats; /* a format code for each column */
/* /*
* Where we store tuples for a held cursor or a PORTAL_UTIL_SELECT query. * Where we store tuples for a held cursor or a PORTAL_ONE_RETURNING or
* (A cursor held past the end of its transaction no longer has any active * PORTAL_UTIL_SELECT query. (A cursor held past the end of its
* executor state.) * transaction no longer has any active executor state.)
*/ */
Tuplestorestate *holdStore; /* store for holdable cursors */ Tuplestorestate *holdStore; /* store for holdable cursors */
MemoryContext holdContext; /* memory containing holdStore */ MemoryContext holdContext; /* memory containing holdStore */
......
--
-- Test INSERT/UPDATE/DELETE RETURNING
--
-- Simple cases
CREATE TEMP TABLE foo (f1 serial, f2 text, f3 int default 42);
NOTICE: CREATE TABLE will create implicit sequence "foo_f1_seq" for serial column "foo.f1"
INSERT INTO foo (f2,f3)
VALUES ('test', DEFAULT), ('More', 11), (upper('more'), 7+9)
RETURNING *, f1+f3 AS sum;
f1 | f2 | f3 | sum
----+------+----+-----
1 | test | 42 | 43
2 | More | 11 | 13
3 | MORE | 16 | 19
(3 rows)
SELECT * FROM foo;
f1 | f2 | f3
----+------+----
1 | test | 42
2 | More | 11
3 | MORE | 16
(3 rows)
UPDATE foo SET f2 = lower(f2), f3 = DEFAULT RETURNING foo.*, f1+f3 AS sum13;
f1 | f2 | f3 | sum13
----+------+----+-------
1 | test | 42 | 43
2 | more | 42 | 44
3 | more | 42 | 45
(3 rows)
SELECT * FROM foo;
f1 | f2 | f3
----+------+----
1 | test | 42
2 | more | 42
3 | more | 42
(3 rows)
DELETE FROM foo WHERE f1 > 2 RETURNING f3, f2, f1, least(f1,f3);
f3 | f2 | f1 | least
----+------+----+-------
42 | more | 3 | 3
(1 row)
SELECT * FROM foo;
f1 | f2 | f3
----+------+----
1 | test | 42
2 | more | 42
(2 rows)
-- Subplans and initplans in the RETURNING list
INSERT INTO foo SELECT f1+10, f2, f3+99 FROM foo
RETURNING *, f1+112 IN (SELECT q1 FROM int8_tbl) AS subplan,
EXISTS(SELECT * FROM int4_tbl) AS initplan;
f1 | f2 | f3 | subplan | initplan
----+------+-----+---------+----------
11 | test | 141 | t | t
12 | more | 141 | f | t
(2 rows)
UPDATE foo SET f3 = f3 * 2
WHERE f1 > 10
RETURNING *, f1+112 IN (SELECT q1 FROM int8_tbl) AS subplan,
EXISTS(SELECT * FROM int4_tbl) AS initplan;
f1 | f2 | f3 | subplan | initplan
----+------+-----+---------+----------
11 | test | 282 | t | t
12 | more | 282 | f | t
(2 rows)
DELETE FROM foo
WHERE f1 > 10
RETURNING *, f1+112 IN (SELECT q1 FROM int8_tbl) AS subplan,
EXISTS(SELECT * FROM int4_tbl) AS initplan;
f1 | f2 | f3 | subplan | initplan
----+------+-----+---------+----------
11 | test | 282 | t | t
12 | more | 282 | f | t
(2 rows)
-- Joins
UPDATE foo SET f3 = f3*2
FROM int4_tbl i
WHERE foo.f1 + 123455 = i.f1
RETURNING foo.*, i.f1 as "i.f1";
f1 | f2 | f3 | i.f1
----+------+----+--------
1 | test | 84 | 123456
(1 row)
SELECT * FROM foo;
f1 | f2 | f3
----+------+----
2 | more | 42
1 | test | 84
(2 rows)
DELETE FROM foo
USING int4_tbl i
WHERE foo.f1 + 123455 = i.f1
RETURNING foo.*, i.f1 as "i.f1";
f1 | f2 | f3 | i.f1
----+------+----+--------
1 | test | 84 | 123456
(1 row)
SELECT * FROM foo;
f1 | f2 | f3
----+------+----
2 | more | 42
(1 row)
-- Check inheritance cases
CREATE TEMP TABLE foochild (fc int) INHERITS (foo);
INSERT INTO foochild VALUES(123,'child',999,-123);
ALTER TABLE foo ADD COLUMN f4 int8 DEFAULT 99;
SELECT * FROM foo;
f1 | f2 | f3 | f4
-----+-------+-----+----
2 | more | 42 | 99
123 | child | 999 | 99
(2 rows)
SELECT * FROM foochild;
f1 | f2 | f3 | fc | f4
-----+-------+-----+------+----
123 | child | 999 | -123 | 99
(1 row)
UPDATE foo SET f4 = f4 + f3 WHERE f4 = 99 RETURNING *;
f1 | f2 | f3 | f4
-----+-------+-----+------
2 | more | 42 | 141
123 | child | 999 | 1098
(2 rows)
SELECT * FROM foo;
f1 | f2 | f3 | f4
-----+-------+-----+------
2 | more | 42 | 141
123 | child | 999 | 1098
(2 rows)
SELECT * FROM foochild;
f1 | f2 | f3 | fc | f4
-----+-------+-----+------+------
123 | child | 999 | -123 | 1098
(1 row)
UPDATE foo SET f3 = f3*2
FROM int8_tbl i
WHERE foo.f1 = i.q1
RETURNING *;
f1 | f2 | f3 | f4 | q1 | q2
-----+-------+------+------+-----+-----
123 | child | 1998 | 1098 | 123 | 456
(1 row)
SELECT * FROM foo;
f1 | f2 | f3 | f4
-----+-------+------+------
2 | more | 42 | 141
123 | child | 1998 | 1098
(2 rows)
SELECT * FROM foochild;
f1 | f2 | f3 | fc | f4
-----+-------+------+------+------
123 | child | 1998 | -123 | 1098
(1 row)
DELETE FROM foo
USING int8_tbl i
WHERE foo.f1 = i.q1
RETURNING *;
f1 | f2 | f3 | f4 | q1 | q2
-----+-------+------+------+-----+-----
123 | child | 1998 | 1098 | 123 | 456
(1 row)
SELECT * FROM foo;
f1 | f2 | f3 | f4
----+------+----+-----
2 | more | 42 | 141
(1 row)
SELECT * FROM foochild;
f1 | f2 | f3 | fc | f4
----+----+----+----+----
(0 rows)
DROP TABLE foochild, foo;
# ---------- # ----------
# The first group of parallel test # The first group of parallel test
# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.33 2006/08/04 00:00:13 tgl Exp $ # $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.34 2006/08/12 02:52:06 tgl Exp $
# ---------- # ----------
test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric
...@@ -75,7 +75,7 @@ test: select_views portals_p2 rules foreign_key cluster dependency guc ...@@ -75,7 +75,7 @@ test: select_views portals_p2 rules foreign_key cluster dependency guc
# The sixth group of parallel test # The sixth group of parallel test
# ---------- # ----------
# "plpgsql" cannot run concurrently with "rules" # "plpgsql" cannot run concurrently with "rules"
test: limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes test: limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning
# run stats by itself because its delay may be insufficient under heavy load # run stats by itself because its delay may be insufficient under heavy load
test: stats test: stats
......
# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.31 2006/08/04 00:00:13 tgl Exp $ # $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.32 2006/08/12 02:52:06 tgl Exp $
# This should probably be in an order similar to parallel_schedule. # This should probably be in an order similar to parallel_schedule.
test: boolean test: boolean
test: char test: char
...@@ -100,5 +100,6 @@ test: alter_table ...@@ -100,5 +100,6 @@ test: alter_table
test: sequence test: sequence
test: polymorphism test: polymorphism
test: rowtypes test: rowtypes
test: returning
test: stats test: stats
test: tablespace test: tablespace
--
-- Test INSERT/UPDATE/DELETE RETURNING
--
-- Simple cases
CREATE TEMP TABLE foo (f1 serial, f2 text, f3 int default 42);
INSERT INTO foo (f2,f3)
VALUES ('test', DEFAULT), ('More', 11), (upper('more'), 7+9)
RETURNING *, f1+f3 AS sum;
SELECT * FROM foo;
UPDATE foo SET f2 = lower(f2), f3 = DEFAULT RETURNING foo.*, f1+f3 AS sum13;
SELECT * FROM foo;
DELETE FROM foo WHERE f1 > 2 RETURNING f3, f2, f1, least(f1,f3);
SELECT * FROM foo;
-- Subplans and initplans in the RETURNING list
INSERT INTO foo SELECT f1+10, f2, f3+99 FROM foo
RETURNING *, f1+112 IN (SELECT q1 FROM int8_tbl) AS subplan,
EXISTS(SELECT * FROM int4_tbl) AS initplan;
UPDATE foo SET f3 = f3 * 2
WHERE f1 > 10
RETURNING *, f1+112 IN (SELECT q1 FROM int8_tbl) AS subplan,
EXISTS(SELECT * FROM int4_tbl) AS initplan;
DELETE FROM foo
WHERE f1 > 10
RETURNING *, f1+112 IN (SELECT q1 FROM int8_tbl) AS subplan,
EXISTS(SELECT * FROM int4_tbl) AS initplan;
-- Joins
UPDATE foo SET f3 = f3*2
FROM int4_tbl i
WHERE foo.f1 + 123455 = i.f1
RETURNING foo.*, i.f1 as "i.f1";
SELECT * FROM foo;
DELETE FROM foo
USING int4_tbl i
WHERE foo.f1 + 123455 = i.f1
RETURNING foo.*, i.f1 as "i.f1";
SELECT * FROM foo;
-- Check inheritance cases
CREATE TEMP TABLE foochild (fc int) INHERITS (foo);
INSERT INTO foochild VALUES(123,'child',999,-123);
ALTER TABLE foo ADD COLUMN f4 int8 DEFAULT 99;
SELECT * FROM foo;
SELECT * FROM foochild;
UPDATE foo SET f4 = f4 + f3 WHERE f4 = 99 RETURNING *;
SELECT * FROM foo;
SELECT * FROM foochild;
UPDATE foo SET f3 = f3*2
FROM int8_tbl i
WHERE foo.f1 = i.q1
RETURNING *;
SELECT * FROM foo;
SELECT * FROM foochild;
DELETE FROM foo
USING int8_tbl i
WHERE foo.f1 = i.q1
RETURNING *;
SELECT * FROM foo;
SELECT * FROM foochild;
DROP TABLE foochild, foo;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment