*** a/doc/src/sgml/ref/delete.sgml
--- b/doc/src/sgml/ref/delete.sgml
***************
*** 21,26 **** PostgreSQL documentation
--- 21,27 ----
  
   <refsynopsisdiv>
  <synopsis>
+ [ WITH [ RECURSIVE ] with_query ]
  DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <replaceable class="parameter">alias</replaceable> ]
      [ USING <replaceable class="PARAMETER">using_list</replaceable> ]
      [ WHERE <replaceable class="PARAMETER">condition</replaceable> | WHERE CURRENT OF <replaceable class="PARAMETER">cursor_name</replaceable> ]
***************
*** 84,89 **** DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ]
--- 85,102 ----
  
    <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 primary query.
+       See <xref linkend="queries-with"> and <xref linkend="sql-select">
+       for details.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><literal>ONLY</></term>
      <listitem>
       <para>
*** a/doc/src/sgml/ref/insert.sgml
--- b/doc/src/sgml/ref/insert.sgml
***************
*** 21,26 **** PostgreSQL documentation
--- 21,27 ----
  
   <refsynopsisdiv>
  <synopsis>
+ [ WITH [ RECURSIVE ] with_query ]
  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> }
      [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
***************
*** 85,90 **** INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable
--- 86,109 ----
  
    <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 primary query.
+       See <xref linkend="queries-with"> and <xref linkend="sql-select">
+       for details.
+      </para>
+      <para>
+       It is possible that <literal>SELECT</literal> query also has
+       <literal>WITH</literal>.  In this case the two
+       <replaceable>with_query</replaceable> can be referred from
+       the <literal>SELECT</literal> query.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><replaceable class="PARAMETER">table</replaceable></term>
      <listitem>
       <para>
*** a/doc/src/sgml/ref/update.sgml
--- b/doc/src/sgml/ref/update.sgml
***************
*** 21,26 **** PostgreSQL documentation
--- 21,27 ----
  
   <refsynopsisdiv>
  <synopsis>
+ [ WITH [ RECURSIVE ] with_query ]
  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 } |
            ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) = ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) } [, ...]
***************
*** 80,85 **** UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep
--- 81,98 ----
  
    <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 primary query.
+       See <xref linkend="queries-with"> and <xref linkend="sql-select">
+       for details.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><replaceable class="PARAMETER">table</replaceable></term>
      <listitem>
       <para>
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2287,2292 **** _copyInsertStmt(InsertStmt *from)
--- 2287,2293 ----
  	COPY_NODE_FIELD(cols);
  	COPY_NODE_FIELD(selectStmt);
  	COPY_NODE_FIELD(returningList);
+ 	COPY_NODE_FIELD(withClause);
  
  	return newnode;
  }
***************
*** 2300,2305 **** _copyDeleteStmt(DeleteStmt *from)
--- 2301,2307 ----
  	COPY_NODE_FIELD(usingClause);
  	COPY_NODE_FIELD(whereClause);
  	COPY_NODE_FIELD(returningList);
+ 	COPY_NODE_FIELD(withClause);
  
  	return newnode;
  }
***************
*** 2314,2319 **** _copyUpdateStmt(UpdateStmt *from)
--- 2316,2322 ----
  	COPY_NODE_FIELD(whereClause);
  	COPY_NODE_FIELD(fromClause);
  	COPY_NODE_FIELD(returningList);
+ 	COPY_NODE_FIELD(withClause);
  
  	return newnode;
  }
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 889,894 **** _equalInsertStmt(InsertStmt *a, InsertStmt *b)
--- 889,895 ----
  	COMPARE_NODE_FIELD(cols);
  	COMPARE_NODE_FIELD(selectStmt);
  	COMPARE_NODE_FIELD(returningList);
+ 	COMPARE_NODE_FIELD(withClause);
  
  	return true;
  }
***************
*** 900,905 **** _equalDeleteStmt(DeleteStmt *a, DeleteStmt *b)
--- 901,907 ----
  	COMPARE_NODE_FIELD(usingClause);
  	COMPARE_NODE_FIELD(whereClause);
  	COMPARE_NODE_FIELD(returningList);
+ 	COMPARE_NODE_FIELD(withClause);
  
  	return true;
  }
***************
*** 912,917 **** _equalUpdateStmt(UpdateStmt *a, UpdateStmt *b)
--- 914,920 ----
  	COMPARE_NODE_FIELD(whereClause);
  	COMPARE_NODE_FIELD(fromClause);
  	COMPARE_NODE_FIELD(returningList);
+ 	COMPARE_NODE_FIELD(withClause);
  
  	return true;
  }
*** a/src/backend/parser/analyze.c
--- b/src/backend/parser/analyze.c
***************
*** 282,287 **** transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
--- 282,294 ----
  
  	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 */
  	qry->resultRelation = setTargetTable(pstate, stmt->relation,
  								  interpretInhOption(stmt->relation->inhOpt),
***************
*** 343,348 **** transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
--- 350,374 ----
  	pstate->p_is_insert = true;
  
  	/*
+ 	 * The WITH of INSERT is interpreted as WITH of SELECT (or VALUES) and
+ 	 * the original WITH of SELECT is appended to that of INSERT.
+ 	 */
+ 	if (stmt->withClause)
+ 	{
+ 		if (selectStmt && selectStmt->withClause)
+ 		{
+ 			WithClause *with = selectStmt->withClause;
+ 			with->recursive |= stmt->withClause->recursive;
+ 			with->ctes = list_concat(copyObject(stmt->withClause->ctes), with->ctes);
+ 		}
+ 		else if (selectStmt)
+ 		{
+ 			selectStmt->withClause = stmt->withClause;
+ 		}
+ 		stmt->withClause = NULL;
+ 	}
+ 
+ 	/*
  	 * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL),
  	 * VALUES list, or general SELECT input.  We special-case VALUES, both for
  	 * efficiency and so we can handle DEFAULT specifications.
***************
*** 1726,1731 **** transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
--- 1752,1764 ----
  	qry->commandType = CMD_UPDATE;
  	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,
  								  interpretInhOption(stmt->relation->inhOpt),
  										 true,
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 430,436 **** static TypeName *TableFuncTypeName(List *columns);
  %type <boolean> xml_whitespace_option
  
  %type <node> 	common_table_expr
! %type <with> 	with_clause
  %type <list>	cte_list
  
  %type <list>	window_clause window_definition_list opt_partition_clause
--- 430,436 ----
  %type <boolean> xml_whitespace_option
  
  %type <node> 	common_table_expr
! %type <with> 	with_clause opt_with_clause
  %type <list>	cte_list
  
  %type <list>	window_clause window_definition_list opt_partition_clause
***************
*** 7129,7139 **** DeallocateStmt: DEALLOCATE name
   *****************************************************************************/
  
  InsertStmt:
! 			INSERT INTO qualified_name insert_rest returning_clause
  				{
! 					$4->relation = $3;
! 					$4->returningList = $5;
! 					$$ = (Node *) $4;
  				}
  		;
  
--- 7129,7140 ----
   *****************************************************************************/
  
  InsertStmt:
! 			opt_with_clause INSERT INTO qualified_name insert_rest returning_clause
  				{
! 					$5->relation = $4;
! 					$5->returningList = $6;
! 					$5->withClause = $1;
! 					$$ = (Node *) $5;
  				}
  		;
  
***************
*** 7189,7202 **** returning_clause:
   *
   *****************************************************************************/
  
! DeleteStmt: DELETE_P FROM relation_expr_opt_alias
  			using_clause where_or_current_clause returning_clause
  				{
  					DeleteStmt *n = makeNode(DeleteStmt);
! 					n->relation = $3;
! 					n->usingClause = $4;
! 					n->whereClause = $5;
! 					n->returningList = $6;
  					$$ = (Node *)n;
  				}
  		;
--- 7190,7204 ----
   *
   *****************************************************************************/
  
! DeleteStmt: opt_with_clause DELETE_P FROM relation_expr_opt_alias
  			using_clause where_or_current_clause returning_clause
  				{
  					DeleteStmt *n = makeNode(DeleteStmt);
! 					n->relation = $4;
! 					n->usingClause = $5;
! 					n->whereClause = $6;
! 					n->returningList = $7;
! 					n->withClause = $1;
  					$$ = (Node *)n;
  				}
  		;
***************
*** 7251,7268 **** opt_nowait:	NOWAIT							{ $$ = TRUE; }
   *
   *****************************************************************************/
  
! UpdateStmt: UPDATE relation_expr_opt_alias
  			SET set_clause_list
  			from_clause
  			where_or_current_clause
  			returning_clause
  				{
  					UpdateStmt *n = makeNode(UpdateStmt);
! 					n->relation = $2;
! 					n->targetList = $4;
! 					n->fromClause = $5;
! 					n->whereClause = $6;
! 					n->returningList = $7;
  					$$ = (Node *)n;
  				}
  		;
--- 7253,7271 ----
   *
   *****************************************************************************/
  
! UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias
  			SET set_clause_list
  			from_clause
  			where_or_current_clause
  			returning_clause
  				{
  					UpdateStmt *n = makeNode(UpdateStmt);
! 					n->relation = $3;
! 					n->targetList = $5;
! 					n->fromClause = $6;
! 					n->whereClause = $7;
! 					n->returningList = $8;
! 					n->withClause = $1;
  					$$ = (Node *)n;
  				}
  		;
***************
*** 7604,7609 **** common_table_expr:  name opt_name_list AS select_with_parens
--- 7607,7618 ----
  			}
  		;
  
+ opt_with_clause:
+ 		with_clause								{ $$ = $1; }
+ 		| /*EMPTY*/								{ $$ = NULL; }
+ 		;
+ 
+ 
  into_clause:
  			INTO OptTempTableName
  				{
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
***************
*** 1865,1870 **** transformRuleStmt(RuleStmt *stmt, const char *queryString,
--- 1865,1878 ----
  			}
  
  			/*
+ 			 * OLD/NEW is not allowed in CTE queries.
+ 			 */
+ 			if (checkCTEHasOldNew(sub_qry))
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 						 errmsg("cannot refer to OLD/NEW in CTE query")));
+ 
+ 			/*
  			 * For efficiency's sake, add OLD to the rule action's jointree
  			 * only if it was actually referenced in the statement or qual.
  			 *
*** a/src/backend/rewrite/rewriteManip.c
--- b/src/backend/rewrite/rewriteManip.c
***************
*** 291,296 **** checkExprHasSubLink_walker(Node *node, void *context)
--- 291,320 ----
  	return expression_tree_walker(node, checkExprHasSubLink_walker, context);
  }
  
+ /*
+  * checkCTEHasOldNew - check if OLD/NEW is referred in CTE queries.
+  */
+ bool
+ checkCTEHasOldNew(Query *node)
+ {
+ 	ListCell   *l;
+ 
+ 	foreach (l, node->cteList)
+ 	{
+ 		CommonTableExpr	   *cte = (CommonTableExpr *) lfirst(l);
+ 		int		new_varno = PRS2_NEW_VARNO;
+ 		int		old_varno = PRS2_OLD_VARNO;
+ 
+ 		/* 1 == the top CTE */
+ 		if (rangeTableEntry_used(cte->ctequery, new_varno, 1))
+ 			return true;
+ 
+ 		if (rangeTableEntry_used(cte->ctequery, old_varno, 1))
+ 			return true;
+ 	}
+ 
+ 	return false;
+ }
  
  /*
   * OffsetVarNodes - adjust Vars when appending one query's RT to another
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 3345,3350 **** get_insert_query_def(Query *query, deparse_context *context)
--- 3345,3353 ----
  	ListCell   *l;
  	List	   *strippedexprs;
  
+ 	/* Insert the WITH clause if given */
+ 	get_with_clause(query, context);
+ 
  	/*
  	 * If it's an INSERT ... SELECT or VALUES (...), (...), ... there will be
  	 * a single RTE for the SELECT or VALUES.
***************
*** 3482,3487 **** get_update_query_def(Query *query, deparse_context *context)
--- 3485,3493 ----
  	RangeTblEntry *rte;
  	ListCell   *l;
  
+ 	/* Insert the WITH clause if given */
+ 	get_with_clause(query, context);
+ 
  	/*
  	 * Start the query with UPDATE relname SET
  	 */
***************
*** 3563,3568 **** get_delete_query_def(Query *query, deparse_context *context)
--- 3569,3577 ----
  	StringInfo	buf = context->buf;
  	RangeTblEntry *rte;
  
+ 	/* Insert the WITH clause if given */
+ 	get_with_clause(query, context);
+ 
  	/*
  	 * Start the query with DELETE FROM relname
  	 */
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 896,901 **** typedef struct InsertStmt
--- 896,902 ----
  	List	   *cols;			/* optional: names of the target columns */
  	Node	   *selectStmt;		/* the source SELECT/VALUES, or NULL */
  	List	   *returningList;	/* list of expressions to return */
+ 	WithClause *withClause;		/* WITH clause */
  } InsertStmt;
  
  /* ----------------------
***************
*** 909,914 **** typedef struct DeleteStmt
--- 910,916 ----
  	List	   *usingClause;	/* optional using clause for more tables */
  	Node	   *whereClause;	/* qualifications */
  	List	   *returningList;	/* list of expressions to return */
+ 	WithClause *withClause;		/* WITH clause */
  } DeleteStmt;
  
  /* ----------------------
***************
*** 923,928 **** typedef struct UpdateStmt
--- 925,931 ----
  	Node	   *whereClause;	/* qualifications */
  	List	   *fromClause;		/* optional from clause for more tables */
  	List	   *returningList;	/* list of expressions to return */
+ 	WithClause *withClause;		/* WITH clause */
  } UpdateStmt;
  
  /* ----------------------
*** a/src/include/rewrite/rewriteManip.h
--- b/src/include/rewrite/rewriteManip.h
***************
*** 56,61 **** extern int	locate_windowfunc(Node *node);
--- 56,62 ----
  extern bool checkExprHasAggs(Node *node);
  extern bool checkExprHasWindowFuncs(Node *node);
  extern bool checkExprHasSubLink(Node *node);
+ extern bool checkCTEHasOldNew(Query *node);
  
  extern Node *replace_rte_variables(Node *node,
  					  int target_varno, int sublevels_up,
*** a/src/test/regress/expected/with.out
--- b/src/test/regress/expected/with.out
***************
*** 738,743 **** WITH RECURSIVE
--- 738,820 ----
  (54 rows)
  
  --
+ -- WITH on top of 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)
+ 
+ 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)
+ 
+ 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)
+ 
+ --
  -- error cases
  --
  -- INTERSECT
***************
*** 774,781 **** WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1)
  ERROR:  recursive reference to query "x" must not appear within its non-recursive term
  LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1)
                                                ^
- CREATE TEMPORARY TABLE y (a INTEGER);
- INSERT INTO y SELECT generate_series(1, 10);
  -- LEFT JOIN
  WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1
  	UNION ALL
--- 851,856 ----
***************
*** 912,917 **** ERROR:  recursive query "foo" column 1 has type numeric(3,0) in non-recursive te
--- 987,997 ----
  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.
+ -- 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/NEW in CTE query
  --
  -- test for bug #4902
  --
*** a/src/test/regress/sql/with.sql
--- b/src/test/regress/sql/with.sql
***************
*** 339,344 **** WITH RECURSIVE
--- 339,371 ----
   SELECT * FROM z;
  
  --
+ -- WITH on top of 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 *;
+ 
+ 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;
+ 
+ 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;
+ 
+ --
  -- error cases
  --
  
***************
*** 364,372 **** WITH RECURSIVE x(n) AS (SELECT n FROM x)
  WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1)
  	SELECT * FROM x;
  
- CREATE TEMPORARY TABLE y (a INTEGER);
- INSERT INTO y SELECT generate_series(1, 10);
- 
  -- LEFT JOIN
  
  WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1
--- 391,396 ----
***************
*** 470,475 **** WITH RECURSIVE foo(i) AS
--- 494,504 ----
     SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10)
  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
  --
