*** ./doc/src/sgml/ref/delete.sgml.orig	2004-12-16 22:14:47.000000000 -0200
--- ./doc/src/sgml/ref/delete.sgml	2004-12-16 20:44:20.000000000 -0200
***************
*** 20,26 ****
  
   <refsynopsisdiv>
  <synopsis>
! DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
  </synopsis>
   </refsynopsisdiv>
  
--- 20,28 ----
  
   <refsynopsisdiv>
  <synopsis>
! DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable>
! 	[ USING <replaceable class="PARAMETER">usinglist</replaceable> ]
! 	[ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
  </synopsis>
   </refsynopsisdiv>
  
***************
*** 50,55 ****
--- 52,65 ----
    </para>
  
    <para>
+    There are two ways to delete rows in a table using information
+    contained in other tables in the database: using sub-selects, or
+    specifying additional tables in the <literal>USING</literal> clause.
+    Which technique is more appropriate depends on the specific
+    circumstances.
+   </para>
+ 
+   <para>
     You must have the <literal>DELETE</literal> privilege on the table
     to delete from it, as well as the <literal>SELECT</literal>
     privilege for any table whose values are read in the <replaceable
***************
*** 71,76 ****
--- 81,101 ----
     </varlistentry>
  
     <varlistentry>
+     <term><replaceable class="PARAMETER">usinglist</replaceable></term>
+     <listitem>
+      <para>
+       A list of table expressions, allowing columns from other tables
+       to appear in the <literal>WHERE</> condition.
+       This is similar to the list of tables that can be
+       specified in the <xref linkend="sql-from"
+       endterm="sql-from-title"> of a <command>SELECT</command>
+       statement; for example, an alias for the table name can be
+       specified.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><replaceable class="parameter">condition</replaceable></term>
      <listitem>
       <para>
***************
*** 122,128 ****
    <title>Compatibility</title>
  
    <para>
!    This command conforms to the SQL standard.
    </para>
   </refsect1>
  </refentry>
--- 147,155 ----
    <title>Compatibility</title>
  
    <para>
!    This command conforms to the SQL standard.  The
!    <literal>USING</literal> clause is a
!    <productname>PostgreSQL</productname> extension.
    </para>
   </refsect1>
  </refentry>
*** ./src/backend/parser/analyze.c.orig	2004-12-16 22:15:26.000000000 -0200
--- ./src/backend/parser/analyze.c	2004-12-16 22:27:31.000000000 -0200
***************
*** 471,476 ****
--- 471,478 ----
  {
  	Query	   *qry = makeNode(Query);
  	Node	   *qual;
+ 	ListCell   *origTargetList;
+ 	ListCell   *tl;
  
  	qry->commandType = CMD_DELETE;
  
***************
*** 482,487 ****
--- 484,498 ----
  
  	qry->distinctClause = NIL;
  
+ 	/*
+ 	 * the USING clause is non-standard SQL syntax. USING clause is used
+ 	 * instead of FROM because we already have FROM clause in DELETE. USING
+ 	 * clause has the same funcionality of FROM clause in UPDATE statement.
+ 	 */
+ 	transformFromClause(pstate, stmt->usingClause);
+ 
+ 	qry->targetList = transformTargetList(pstate, stmt->targetList);
+ 
  	/* fix where clause */
  	qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
  
***************
*** 494,499 ****
--- 505,555 ----
  	if (pstate->p_hasAggs)
  		parseCheckAggregates(pstate, qry);
  
+ 	/*
+ 	 * Now we are done with SELECT-like processing, and can get on with
+ 	 * transforming the target list to match the DELETE target columns.
+ 	 */
+ 
+ 	/* Prepare to assign non-conflicting resnos to resjunk attributes */
+ 	if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts)
+ 		pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
+ 
+ 	/* Prepare non-junk columns for assignment to target table */
+ 	origTargetList = list_head(stmt->targetList);
+ 
+ 	foreach(tl, qry->targetList)
+ 	{
+ 		TargetEntry *tle = (TargetEntry *) lfirst(tl);
+ 		Resdom	   *resnode = tle->resdom;
+ 		ResTarget  *origTarget;
+ 
+ 		if (resnode->resjunk)
+ 		{
+ 			/*
+ 			 * Resjunk nodes need no additional processing, but be sure
+ 			 * they have resnos that do not match any target columns; else
+ 			 * rewriter or planner might get confused. They don't need a
+ 			 * resname either.
+ 			 */
+ 			resnode->resno = (AttrNumber) pstate->p_next_resno++;
+ 			resnode->resname = NULL;
+ 			continue;
+ 		}
+ 		if (origTargetList == NULL)
+ 			elog(ERROR, "DELETE target count mismatch --- internal error");
+ 		origTarget = (ResTarget *) lfirst(origTargetList);
+ 		Assert(IsA(origTarget, ResTarget));
+ 
+ 		updateTargetListEntry(pstate, tle, origTarget->name,
+ 				  attnameAttNum(pstate->p_target_relation,
+ 						origTarget->name, true),
+ 				  origTarget->indirection);
+ 
+ 		origTargetList = lnext(origTargetList);
+ 	}
+ 	if (origTargetList != NULL)
+ 		elog(ERROR, "DELETE target count mismatch --- internal error");
+ 
  	return qry;
  }
  
*** ./src/backend/parser/gram.y.orig	2004-12-16 22:15:44.000000000 -0200
--- ./src/backend/parser/gram.y	2004-12-16 23:38:55.000000000 -0200
***************
*** 227,233 ****
  				transaction_mode_list_or_empty
  				TableFuncElementList
  				prep_type_clause prep_type_list
! 				execute_param_clause
  
  %type <range>	into_clause OptTempTableName
  
--- 227,233 ----
  				transaction_mode_list_or_empty
  				TableFuncElementList
  				prep_type_clause prep_type_list
! 				execute_param_clause using_clause
  
  %type <range>	into_clause OptTempTableName
  
***************
*** 4646,4656 ****
   *
   *****************************************************************************/
  
! DeleteStmt: DELETE_P FROM relation_expr where_clause
  				{
  					DeleteStmt *n = makeNode(DeleteStmt);
  					n->relation = $3;
! 					n->whereClause = $4;
  					$$ = (Node *)n;
  				}
  		;
--- 4646,4657 ----
   *
   *****************************************************************************/
  
! DeleteStmt: DELETE_P FROM relation_expr using_clause where_clause
  				{
  					DeleteStmt *n = makeNode(DeleteStmt);
  					n->relation = $3;
! 					n->usingClause = $4;
! 					n->whereClause = $5;
  					$$ = (Node *)n;
  				}
  		;
***************
*** 5085,5090 ****
--- 5086,5105 ----
  			| from_list ',' table_ref				{ $$ = lappend($1, $3); }
  		;
  
+ /*****************************************************************************
+  *
+  *	using_clause	- allow list of JOIN expressions in DELETE statements
+  *
+  *	Here we don't declare using_list because it's the same as from_list,
+  *	so we just use it.
+  *
+  *****************************************************************************/
+ 
+ using_clause:
+ 	    		USING from_list						{ $$ = $2; }
+ 			| /*EMPTY*/								{ $$ = NIL; }
+ 		;
+ 
  /*
   * table_ref is where an alias clause can be attached.	Note we cannot make
   * alias_clause have an empty production because that causes parse conflicts
*** ./src/bin/psql/tab-complete.c.orig	2004-12-16 22:16:08.000000000 -0200
--- ./src/bin/psql/tab-complete.c	2004-12-16 20:44:20.000000000 -0200
***************
*** 1162,1170 ****
  	else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 &&
  			 pg_strcasecmp(prev_wd, "FROM") == 0)
  		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
! 	/* Complete DELETE FROM <table> with "WHERE" (perhaps a safe idea?) */
  	else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 &&
! 			 pg_strcasecmp(prev2_wd, "FROM") == 0)
  		COMPLETE_WITH_CONST("WHERE");
  
  /* EXPLAIN */
--- 1162,1185 ----
  	else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 &&
  			 pg_strcasecmp(prev_wd, "FROM") == 0)
  		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
! 	/* Complete DELETE FROM <table> */
  	else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 &&
! 			pg_strcasecmp(prev2_wd, "FROM") == 0)
! 	{
! 		static const char *const list_DELETE[] =
! 		{"USING", "WHERE", NULL};
! 
! 		COMPLETE_WITH_LIST(list_DELETE);
! 	}
! 	/* TODO: Complete DELETE FROM <table> USING <table1> */
! 	/*
! 	 * Complete DELETE FROM <table> with "WHERE" (perhaps a safe idea?)
! 	 * Is it safe to check only "FROM" and "USING" here?
! 	 */
! 	else if ((pg_strcasecmp(prev3_wd, "DELETE") == 0 &&
! 			 pg_strcasecmp(prev2_wd, "FROM") == 0) ||
! 			(pg_strcasecmp(prev4_wd, "FROM") == 0 &&
! 			 pg_strcasecmp(prev2_wd, "USING") == 0))
  		COMPLETE_WITH_CONST("WHERE");
  
  /* EXPLAIN */
*** ./src/include/nodes/parsenodes.h.orig	2004-12-16 22:16:29.000000000 -0200
--- ./src/include/nodes/parsenodes.h	2004-12-16 20:44:20.000000000 -0200
***************
*** 611,617 ****
--- 611,619 ----
  {
  	NodeTag		type;
  	RangeVar   *relation;		/* relation to delete from */
+ 	List		*targetList;	/* the target list (of ResTarget) */
  	Node	   *whereClause;	/* qualifications */
+ 	List		*usingClause;	/* optional using clause for more tables */
  } DeleteStmt;
  
  /* ----------------------
