Commit 07f1264d authored by Tom Lane's avatar Tom Lane

Allow WITH clauses to be attached to INSERT, UPDATE, DELETE statements.

This is not the hoped-for facility of using INSERT/UPDATE/DELETE inside
a WITH, but rather the other way around.  It seems useful in its own
right anyway.

Note: catversion bumped because, although the contents of stored rules
might look compatible, there's actually a subtle semantic change.
A single Query containing a WITH and INSERT...VALUES now represents
writing the WITH before the INSERT, not before the VALUES.  While it's
not clear that that matters to anyone, it seems like a good idea to
have it cited in the git history for catversion.h.

Original patch by Marko Tiikkaja, with updating and cleanup by
Hitoshi Harada.
parent 6ab42ae3
...@@ -472,7 +472,7 @@ FROM <replaceable>table_reference</replaceable> <optional>, <replaceable>table_r ...@@ -472,7 +472,7 @@ FROM <replaceable>table_reference</replaceable> <optional>, <replaceable>table_r
(1 row) (1 row)
</screen> </screen>
This is because a restriction placed in the <literal>ON</> This is because a restriction placed in the <literal>ON</>
clause is processed <emphasis>before</> the join, while clause is processed <emphasis>before</> the join, while
a restriction placed in the <literal>WHERE</> clause is processed a restriction placed in the <literal>WHERE</> clause is processed
<emphasis>after</> the join. <emphasis>after</> the join.
</para> </para>
...@@ -1139,7 +1139,7 @@ SELECT a "value", b + c AS sum FROM ... ...@@ -1139,7 +1139,7 @@ SELECT a "value", b + c AS sum FROM ...
<para> <para>
The naming of output columns here is different from that done in The naming of output columns here is different from that done in
the <literal>FROM</> clause (see <xref the <literal>FROM</> clause (see <xref
linkend="queries-table-aliases">). It is possible linkend="queries-table-aliases">). It is possible
to rename the same column twice, but the name assigned in to rename the same column twice, but the name assigned in
the select list is the one that will be passed on. the select list is the one that will be passed on.
</para> </para>
...@@ -1539,7 +1539,7 @@ SELECT <replaceable>select_list</replaceable> FROM <replaceable>table_expression ...@@ -1539,7 +1539,7 @@ SELECT <replaceable>select_list</replaceable> FROM <replaceable>table_expression
<para> <para>
<literal>WITH</> provides a way to write subqueries for use in a larger <literal>WITH</> provides a way to write subqueries for use in a larger
<literal>SELECT</> query. The subqueries can be thought of as defining query. The subqueries can be thought of as defining
temporary tables that exist just for this query. One use of this feature temporary tables that exist just for this query. One use of this feature
is to break down complicated queries into simpler parts. An example is: is to break down complicated queries into simpler parts. An example is:
...@@ -1791,12 +1791,20 @@ SELECT n FROM t LIMIT 100; ...@@ -1791,12 +1791,20 @@ SELECT n FROM t LIMIT 100;
However, the other side of this coin is that the optimizer is less able to However, the other side of this coin is that the optimizer is less able to
push restrictions from the parent query down into a <literal>WITH</> query push restrictions from the parent query down into a <literal>WITH</> query
than an ordinary sub-query. The <literal>WITH</> query will generally be than an ordinary sub-query. The <literal>WITH</> query will generally be
evaluated as stated, without suppression of rows that the parent query evaluated as written, without suppression of rows that the parent query
might discard afterwards. (But, as mentioned above, evaluation might stop might discard afterwards. (But, as mentioned above, evaluation might stop
early if the reference(s) to the query demand only a limited number of early if the reference(s) to the query demand only a limited number of
rows.) rows.)
</para> </para>
<para>
The examples above only show <literal>WITH</> being used with
<command>SELECT</>, but it can be attached in the same way to
<command>INSERT</>, <command>UPDATE</>, or <command>DELETE</>.
In each case it effectively provides temporary table(s) that can
be referred to in the main command.
</para>
</sect1> </sect1>
</chapter> </chapter>
...@@ -21,6 +21,7 @@ PostgreSQL documentation ...@@ -21,6 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
[ WITH [ RECURSIVE ] <replaceable class="parameter">with_query</replaceable> [, ...] ]
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">using_list</replaceable> ] [ USING <replaceable class="PARAMETER">using_list</replaceable> ]
[ WHERE <replaceable class="PARAMETER">condition</replaceable> | WHERE CURRENT OF <replaceable class="PARAMETER">cursor_name</replaceable> ] [ WHERE <replaceable class="PARAMETER">condition</replaceable> | WHERE CURRENT OF <replaceable class="PARAMETER">cursor_name</replaceable> ]
...@@ -83,6 +84,18 @@ DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] ...@@ -83,6 +84,18 @@ DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ]
<title>Parameters</title> <title>Parameters</title>
<variablelist> <variablelist>
<varlistentry>
<term><replaceable class="parameter">with_query</replaceable></term>
<listitem>
<para>
The <literal>WITH</literal> clause allows you to specify one or more
subqueries that can be referenced by name in the <command>DELETE</>
query. See <xref linkend="queries-with"> and <xref linkend="sql-select">
for details.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><literal>ONLY</></term> <term><literal>ONLY</></term>
<listitem> <listitem>
...@@ -272,7 +285,8 @@ DELETE FROM tasks WHERE CURRENT OF c_tasks; ...@@ -272,7 +285,8 @@ DELETE FROM tasks WHERE CURRENT OF c_tasks;
<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>USING</literal> and <literal>RETURNING</> clauses that the <literal>USING</literal> and <literal>RETURNING</> clauses
are <productname>PostgreSQL</productname> extensions. are <productname>PostgreSQL</productname> extensions, as is the ability
to use <literal>WITH</> with <command>DELETE</>.
</para> </para>
</refsect1> </refsect1>
</refentry> </refentry>
...@@ -21,6 +21,7 @@ PostgreSQL documentation ...@@ -21,6 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
[ WITH [ RECURSIVE ] <replaceable class="parameter">with_query</replaceable> [, ...] ]
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> ] [, ...] ] [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
...@@ -84,6 +85,26 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable ...@@ -84,6 +85,26 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable
<title>Parameters</title> <title>Parameters</title>
<variablelist> <variablelist>
<varlistentry>
<term><replaceable class="parameter">with_query</replaceable></term>
<listitem>
<para>
The <literal>WITH</literal> clause allows you to specify one or more
subqueries that can be referenced by name in the <command>INSERT</>
query. See <xref linkend="queries-with"> and <xref linkend="sql-select">
for details.
</para>
<para>
It is possible for the <replaceable class="parameter">query</replaceable>
(<command>SELECT</command> statement)
to also contain a <literal>WITH</literal> clause. In such a case both
sets of <replaceable>with_query</replaceable> can be referenced within
the <replaceable class="parameter">query</replaceable>, but the
second one takes precedence since it is more closely nested.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><replaceable class="PARAMETER">table</replaceable></term> <term><replaceable class="PARAMETER">table</replaceable></term>
<listitem> <listitem>
...@@ -287,7 +308,9 @@ INSERT INTO distributors (did, dname) VALUES (DEFAULT, 'XYZ Widgets') ...@@ -287,7 +308,9 @@ INSERT INTO distributors (did, dname) VALUES (DEFAULT, 'XYZ Widgets')
<para> <para>
<command>INSERT</command> conforms to the SQL standard, except that <command>INSERT</command> conforms to the SQL standard, except that
the <literal>RETURNING</> clause is a the <literal>RETURNING</> clause is a
<productname>PostgreSQL</productname> extension. Also, the case in <productname>PostgreSQL</productname> extension, as is the ability
to use <literal>WITH</> with <command>INSERT</>.
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.
......
...@@ -21,6 +21,7 @@ PostgreSQL documentation ...@@ -21,6 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
[ WITH [ RECURSIVE ] <replaceable class="parameter">with_query</replaceable> [, ...] ]
UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <replaceable class="parameter">alias</replaceable> ] UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <replaceable class="parameter">alias</replaceable> ]
SET { <replaceable class="PARAMETER">column</replaceable> = { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } | SET { <replaceable class="PARAMETER">column</replaceable> = { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } |
( <replaceable class="PARAMETER">column</replaceable> [, ...] ) = ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) } [, ...] ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) = ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) } [, ...]
...@@ -79,6 +80,18 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep ...@@ -79,6 +80,18 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep
<title>Parameters</title> <title>Parameters</title>
<variablelist> <variablelist>
<varlistentry>
<term><replaceable class="parameter">with_query</replaceable></term>
<listitem>
<para>
The <literal>WITH</literal> clause allows you to specify one or more
subqueries that can be referenced by name in the <command>UPDATE</>
query. See <xref linkend="queries-with"> and <xref linkend="sql-select">
for details.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><replaceable class="PARAMETER">table</replaceable></term> <term><replaceable class="PARAMETER">table</replaceable></term>
<listitem> <listitem>
...@@ -345,7 +358,8 @@ UPDATE films SET kind = 'Dramatic' WHERE CURRENT OF c_films; ...@@ -345,7 +358,8 @@ UPDATE films SET kind = 'Dramatic' WHERE CURRENT OF c_films;
<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> and <literal>RETURNING</> clauses that the <literal>FROM</literal> and <literal>RETURNING</> clauses
are <productname>PostgreSQL</productname> extensions. are <productname>PostgreSQL</productname> extensions, as is the ability
to use <literal>WITH</> with <command>UPDATE</>.
</para> </para>
<para> <para>
......
...@@ -2313,6 +2313,7 @@ _copyInsertStmt(InsertStmt *from) ...@@ -2313,6 +2313,7 @@ _copyInsertStmt(InsertStmt *from)
COPY_NODE_FIELD(cols); COPY_NODE_FIELD(cols);
COPY_NODE_FIELD(selectStmt); COPY_NODE_FIELD(selectStmt);
COPY_NODE_FIELD(returningList); COPY_NODE_FIELD(returningList);
COPY_NODE_FIELD(withClause);
return newnode; return newnode;
} }
...@@ -2326,6 +2327,7 @@ _copyDeleteStmt(DeleteStmt *from) ...@@ -2326,6 +2327,7 @@ _copyDeleteStmt(DeleteStmt *from)
COPY_NODE_FIELD(usingClause); COPY_NODE_FIELD(usingClause);
COPY_NODE_FIELD(whereClause); COPY_NODE_FIELD(whereClause);
COPY_NODE_FIELD(returningList); COPY_NODE_FIELD(returningList);
COPY_NODE_FIELD(withClause);
return newnode; return newnode;
} }
...@@ -2340,6 +2342,7 @@ _copyUpdateStmt(UpdateStmt *from) ...@@ -2340,6 +2342,7 @@ _copyUpdateStmt(UpdateStmt *from)
COPY_NODE_FIELD(whereClause); COPY_NODE_FIELD(whereClause);
COPY_NODE_FIELD(fromClause); COPY_NODE_FIELD(fromClause);
COPY_NODE_FIELD(returningList); COPY_NODE_FIELD(returningList);
COPY_NODE_FIELD(withClause);
return newnode; return newnode;
} }
......
...@@ -890,6 +890,7 @@ _equalInsertStmt(InsertStmt *a, InsertStmt *b) ...@@ -890,6 +890,7 @@ _equalInsertStmt(InsertStmt *a, InsertStmt *b)
COMPARE_NODE_FIELD(cols); COMPARE_NODE_FIELD(cols);
COMPARE_NODE_FIELD(selectStmt); COMPARE_NODE_FIELD(selectStmt);
COMPARE_NODE_FIELD(returningList); COMPARE_NODE_FIELD(returningList);
COMPARE_NODE_FIELD(withClause);
return true; return true;
} }
...@@ -901,6 +902,7 @@ _equalDeleteStmt(DeleteStmt *a, DeleteStmt *b) ...@@ -901,6 +902,7 @@ _equalDeleteStmt(DeleteStmt *a, DeleteStmt *b)
COMPARE_NODE_FIELD(usingClause); COMPARE_NODE_FIELD(usingClause);
COMPARE_NODE_FIELD(whereClause); COMPARE_NODE_FIELD(whereClause);
COMPARE_NODE_FIELD(returningList); COMPARE_NODE_FIELD(returningList);
COMPARE_NODE_FIELD(withClause);
return true; return true;
} }
...@@ -913,6 +915,7 @@ _equalUpdateStmt(UpdateStmt *a, UpdateStmt *b) ...@@ -913,6 +915,7 @@ _equalUpdateStmt(UpdateStmt *a, UpdateStmt *b)
COMPARE_NODE_FIELD(whereClause); COMPARE_NODE_FIELD(whereClause);
COMPARE_NODE_FIELD(fromClause); COMPARE_NODE_FIELD(fromClause);
COMPARE_NODE_FIELD(returningList); COMPARE_NODE_FIELD(returningList);
COMPARE_NODE_FIELD(withClause);
return true; return true;
} }
......
...@@ -283,6 +283,13 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) ...@@ -283,6 +283,13 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
qry->commandType = CMD_DELETE; qry->commandType = CMD_DELETE;
/* process the WITH clause independently of all else */
if (stmt->withClause)
{
qry->hasRecursive = stmt->withClause->recursive;
qry->cteList = transformWithClause(pstate, stmt->withClause);
}
/* set up range table with just the result rel */ /* set up range table with just the result rel */
qry->resultRelation = setTargetTable(pstate, stmt->relation, qry->resultRelation = setTargetTable(pstate, stmt->relation,
interpretInhOption(stmt->relation->inhOpt), interpretInhOption(stmt->relation->inhOpt),
...@@ -340,9 +347,19 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ...@@ -340,9 +347,19 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
ListCell *attnos; ListCell *attnos;
ListCell *lc; ListCell *lc;
/* There can't be any outer WITH to worry about */
Assert(pstate->p_ctenamespace == NIL);
qry->commandType = CMD_INSERT; qry->commandType = CMD_INSERT;
pstate->p_is_insert = true; pstate->p_is_insert = true;
/* process the WITH clause independently of all else */
if (stmt->withClause)
{
qry->hasRecursive = stmt->withClause->recursive;
qry->cteList = transformWithClause(pstate, stmt->withClause);
}
/* /*
* We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL), * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL),
* VALUES list, or general SELECT input. We special-case VALUES, both for * VALUES list, or general SELECT input. We special-case VALUES, both for
...@@ -376,8 +393,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ...@@ -376,8 +393,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
pstate->p_relnamespace = NIL; pstate->p_relnamespace = NIL;
sub_varnamespace = pstate->p_varnamespace; sub_varnamespace = pstate->p_varnamespace;
pstate->p_varnamespace = NIL; pstate->p_varnamespace = NIL;
/* There can't be any outer WITH to worry about */
Assert(pstate->p_ctenamespace == NIL);
} }
else else
{ {
...@@ -518,13 +533,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ...@@ -518,13 +533,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
List *exprsLists = NIL; List *exprsLists = NIL;
int sublist_length = -1; int sublist_length = -1;
/* process the WITH clause */
if (selectStmt->withClause)
{
qry->hasRecursive = selectStmt->withClause->recursive;
qry->cteList = transformWithClause(pstate, selectStmt->withClause);
}
foreach(lc, selectStmt->valuesLists) foreach(lc, selectStmt->valuesLists)
{ {
List *sublist = (List *) lfirst(lc); List *sublist = (List *) lfirst(lc);
...@@ -618,13 +626,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ...@@ -618,13 +626,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
Assert(list_length(valuesLists) == 1); Assert(list_length(valuesLists) == 1);
/* process the WITH clause */
if (selectStmt->withClause)
{
qry->hasRecursive = selectStmt->withClause->recursive;
qry->cteList = transformWithClause(pstate, selectStmt->withClause);
}
/* Do basic expression transformation (same as a ROW() expr) */ /* Do basic expression transformation (same as a ROW() expr) */
exprList = transformExpressionList(pstate, exprList = transformExpressionList(pstate,
(List *) linitial(valuesLists)); (List *) linitial(valuesLists));
...@@ -1794,6 +1795,13 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) ...@@ -1794,6 +1795,13 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
qry->commandType = CMD_UPDATE; qry->commandType = CMD_UPDATE;
pstate->p_is_update = true; pstate->p_is_update = true;
/* process the WITH clause independently of all else */
if (stmt->withClause)
{
qry->hasRecursive = stmt->withClause->recursive;
qry->cteList = transformWithClause(pstate, stmt->withClause);
}
qry->resultRelation = setTargetTable(pstate, stmt->relation, qry->resultRelation = setTargetTable(pstate, stmt->relation,
interpretInhOption(stmt->relation->inhOpt), interpretInhOption(stmt->relation->inhOpt),
true, true,
......
...@@ -433,7 +433,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ ...@@ -433,7 +433,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
%type <boolean> xml_whitespace_option %type <boolean> xml_whitespace_option
%type <node> common_table_expr %type <node> common_table_expr
%type <with> with_clause %type <with> with_clause opt_with_clause
%type <list> cte_list %type <list> cte_list
%type <list> window_clause window_definition_list opt_partition_clause %type <list> window_clause window_definition_list opt_partition_clause
...@@ -7269,11 +7269,12 @@ DeallocateStmt: DEALLOCATE name ...@@ -7269,11 +7269,12 @@ DeallocateStmt: DEALLOCATE name
*****************************************************************************/ *****************************************************************************/
InsertStmt: InsertStmt:
INSERT INTO qualified_name insert_rest returning_clause opt_with_clause INSERT INTO qualified_name insert_rest returning_clause
{ {
$4->relation = $3; $5->relation = $4;
$4->returningList = $5; $5->returningList = $6;
$$ = (Node *) $4; $5->withClause = $1;
$$ = (Node *) $5;
} }
; ;
...@@ -7329,14 +7330,15 @@ returning_clause: ...@@ -7329,14 +7330,15 @@ returning_clause:
* *
*****************************************************************************/ *****************************************************************************/
DeleteStmt: DELETE_P FROM relation_expr_opt_alias DeleteStmt: opt_with_clause DELETE_P FROM relation_expr_opt_alias
using_clause where_or_current_clause returning_clause using_clause where_or_current_clause returning_clause
{ {
DeleteStmt *n = makeNode(DeleteStmt); DeleteStmt *n = makeNode(DeleteStmt);
n->relation = $3; n->relation = $4;
n->usingClause = $4; n->usingClause = $5;
n->whereClause = $5; n->whereClause = $6;
n->returningList = $6; n->returningList = $7;
n->withClause = $1;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
...@@ -7391,18 +7393,19 @@ opt_nowait: NOWAIT { $$ = TRUE; } ...@@ -7391,18 +7393,19 @@ opt_nowait: NOWAIT { $$ = TRUE; }
* *
*****************************************************************************/ *****************************************************************************/
UpdateStmt: UPDATE relation_expr_opt_alias UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias
SET set_clause_list SET set_clause_list
from_clause from_clause
where_or_current_clause where_or_current_clause
returning_clause returning_clause
{ {
UpdateStmt *n = makeNode(UpdateStmt); UpdateStmt *n = makeNode(UpdateStmt);
n->relation = $2; n->relation = $3;
n->targetList = $4; n->targetList = $5;
n->fromClause = $5; n->fromClause = $6;
n->whereClause = $6; n->whereClause = $7;
n->returningList = $7; n->returningList = $8;
n->withClause = $1;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
...@@ -7744,6 +7747,11 @@ common_table_expr: name opt_name_list AS select_with_parens ...@@ -7744,6 +7747,11 @@ common_table_expr: name opt_name_list AS select_with_parens
} }
; ;
opt_with_clause:
with_clause { $$ = $1; }
| /*EMPTY*/ { $$ = NULL; }
;
into_clause: into_clause:
INTO OptTempTableName INTO OptTempTableName
{ {
......
...@@ -1867,6 +1867,35 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, ...@@ -1867,6 +1867,35 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
break; break;
} }
/*
* OLD/NEW are not allowed in WITH queries, because they would
* amount to outer references for the WITH, which we disallow.
* However, they were already in the outer rangetable when we
* analyzed the query, so we have to check.
*
* Note that in the INSERT...SELECT case, we need to examine
* the CTE lists of both top_subqry and sub_qry.
*
* Note that we aren't digging into the body of the query
* looking for WITHs in nested sub-SELECTs. A WITH down there
* can legitimately refer to OLD/NEW, because it'd be an
* indirect-correlated outer reference.
*/
if (rangeTableEntry_used((Node *) top_subqry->cteList,
PRS2_OLD_VARNO, 0) ||
rangeTableEntry_used((Node *) sub_qry->cteList,
PRS2_OLD_VARNO, 0))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot refer to OLD within WITH query")));
if (rangeTableEntry_used((Node *) top_subqry->cteList,
PRS2_NEW_VARNO, 0) ||
rangeTableEntry_used((Node *) sub_qry->cteList,
PRS2_NEW_VARNO, 0))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot refer to NEW within WITH query")));
/* /*
* For efficiency's sake, add OLD to the rule action's jointree * For efficiency's sake, add OLD to the rule action's jointree
* only if it was actually referenced in the statement or qual. * only if it was actually referenced in the statement or qual.
......
...@@ -3352,6 +3352,9 @@ get_insert_query_def(Query *query, deparse_context *context) ...@@ -3352,6 +3352,9 @@ get_insert_query_def(Query *query, deparse_context *context)
ListCell *l; ListCell *l;
List *strippedexprs; List *strippedexprs;
/* Insert the WITH clause if given */
get_with_clause(query, context);
/* /*
* If it's an INSERT ... SELECT or VALUES (...), (...), ... there will be * If it's an INSERT ... SELECT or VALUES (...), (...), ... there will be
* a single RTE for the SELECT or VALUES. * a single RTE for the SELECT or VALUES.
...@@ -3451,15 +3454,11 @@ get_insert_query_def(Query *query, deparse_context *context) ...@@ -3451,15 +3454,11 @@ get_insert_query_def(Query *query, deparse_context *context)
} }
else if (values_rte) else if (values_rte)
{ {
/* A WITH clause is possible here */
get_with_clause(query, context);
/* Add the multi-VALUES expression lists */ /* Add the multi-VALUES expression lists */
get_values_def(values_rte->values_lists, context); get_values_def(values_rte->values_lists, context);
} }
else else
{ {
/* A WITH clause is possible here */
get_with_clause(query, context);
/* Add the single-VALUES expression list */ /* Add the single-VALUES expression list */
appendContextKeyword(context, "VALUES (", appendContextKeyword(context, "VALUES (",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 2); -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
...@@ -3489,6 +3488,9 @@ get_update_query_def(Query *query, deparse_context *context) ...@@ -3489,6 +3488,9 @@ get_update_query_def(Query *query, deparse_context *context)
RangeTblEntry *rte; RangeTblEntry *rte;
ListCell *l; ListCell *l;
/* Insert the WITH clause if given */
get_with_clause(query, context);
/* /*
* Start the query with UPDATE relname SET * Start the query with UPDATE relname SET
*/ */
...@@ -3570,6 +3572,9 @@ get_delete_query_def(Query *query, deparse_context *context) ...@@ -3570,6 +3572,9 @@ get_delete_query_def(Query *query, deparse_context *context)
StringInfo buf = context->buf; StringInfo buf = context->buf;
RangeTblEntry *rte; RangeTblEntry *rte;
/* Insert the WITH clause if given */
get_with_clause(query, context);
/* /*
* Start the query with DELETE FROM relname * Start the query with DELETE FROM relname
*/ */
......
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201010101 #define CATALOG_VERSION_NO 201010151
#endif #endif
...@@ -896,6 +896,7 @@ typedef struct InsertStmt ...@@ -896,6 +896,7 @@ typedef struct InsertStmt
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 */ List *returningList; /* list of expressions to return */
WithClause *withClause; /* WITH clause */
} InsertStmt; } InsertStmt;
/* ---------------------- /* ----------------------
...@@ -909,6 +910,7 @@ typedef struct DeleteStmt ...@@ -909,6 +910,7 @@ typedef struct DeleteStmt
List *usingClause; /* optional using clause for more tables */ List *usingClause; /* optional using clause for more tables */
Node *whereClause; /* qualifications */ Node *whereClause; /* qualifications */
List *returningList; /* list of expressions to return */ List *returningList; /* list of expressions to return */
WithClause *withClause; /* WITH clause */
} DeleteStmt; } DeleteStmt;
/* ---------------------- /* ----------------------
...@@ -923,6 +925,7 @@ typedef struct UpdateStmt ...@@ -923,6 +925,7 @@ typedef struct UpdateStmt
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 */ List *returningList; /* list of expressions to return */
WithClause *withClause; /* WITH clause */
} UpdateStmt; } UpdateStmt;
/* ---------------------- /* ----------------------
......
...@@ -737,6 +737,134 @@ WITH RECURSIVE ...@@ -737,6 +737,134 @@ WITH RECURSIVE
10 10
(54 rows) (54 rows)
--
-- Test WITH attached to a DML statement
--
CREATE TEMPORARY TABLE y (a INTEGER);
INSERT INTO y SELECT generate_series(1, 10);
WITH t AS (
SELECT a FROM y
)
INSERT INTO y
SELECT a+20 FROM t RETURNING *;
a
----
21
22
23
24
25
26
27
28
29
30
(10 rows)
SELECT * FROM y;
a
----
1
2
3
4
5
6
7
8
9
10
21
22
23
24
25
26
27
28
29
30
(20 rows)
WITH t AS (
SELECT a FROM y
)
UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a;
a
----
11
12
13
14
15
16
17
18
19
20
(10 rows)
SELECT * FROM y;
a
----
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(20 rows)
WITH RECURSIVE t(a) AS (
SELECT 11
UNION ALL
SELECT a+1 FROM t WHERE a < 50
)
DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a;
a
----
11
12
13
14
15
16
17
18
19
20
(10 rows)
SELECT * FROM y;
a
----
1
2
3
4
5
6
7
8
9
10
(10 rows)
DROP TABLE y;
-- --
-- error cases -- error cases
-- --
...@@ -912,6 +1040,11 @@ ERROR: recursive query "foo" column 1 has type numeric(3,0) in non-recursive te ...@@ -912,6 +1040,11 @@ ERROR: recursive query "foo" column 1 has type numeric(3,0) in non-recursive te
LINE 2: (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) LINE 2: (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i)
^ ^
HINT: Cast the output of the non-recursive term to the correct type. HINT: Cast the output of the non-recursive term to the correct type.
-- disallow OLD/NEW reference in CTE
CREATE TEMPORARY TABLE x (n integer);
CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD
WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t;
ERROR: cannot refer to OLD within WITH query
-- --
-- test for bug #4902 -- test for bug #4902
-- --
......
...@@ -338,6 +338,39 @@ WITH RECURSIVE ...@@ -338,6 +338,39 @@ WITH RECURSIVE
(SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10) (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10)
SELECT * FROM z; SELECT * FROM z;
--
-- Test WITH attached to a DML statement
--
CREATE TEMPORARY TABLE y (a INTEGER);
INSERT INTO y SELECT generate_series(1, 10);
WITH t AS (
SELECT a FROM y
)
INSERT INTO y
SELECT a+20 FROM t RETURNING *;
SELECT * FROM y;
WITH t AS (
SELECT a FROM y
)
UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a;
SELECT * FROM y;
WITH RECURSIVE t(a) AS (
SELECT 11
UNION ALL
SELECT a+1 FROM t WHERE a < 50
)
DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a;
SELECT * FROM y;
DROP TABLE y;
-- --
-- error cases -- error cases
-- --
...@@ -470,6 +503,11 @@ WITH RECURSIVE foo(i) AS ...@@ -470,6 +503,11 @@ WITH RECURSIVE foo(i) AS
SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10)
SELECT * FROM foo; SELECT * FROM foo;
-- disallow OLD/NEW reference in CTE
CREATE TEMPORARY TABLE x (n integer);
CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD
WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t;
-- --
-- test for bug #4902 -- test for bug #4902
-- --
......
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