Hi,

As per discussion on -hackers, a patch which allows updates to use
subselects is attached with this mail.

As per discussion with Tom, I have adopted the following approach:

* Introduce ROWEXPR_SUBLINK type for subqueries that allows multiple column
outputs.
* Populate the targetList with PARAM_SUBLINK entries dependent on the
subselects.
* Modify the targets in-place into PARAM_EXEC entries in the make_subplan
phase.

The above does not require any kluges in the targetList processing code path
at all.

UPDATEs seem to work fine using subselects with this patch. I have modified
the update.sql regression test to include possible variations .

No documentation changes are present in this patch.
Feedback, comments appreciated.

Regards,
Nikhils
--
EnterpriseDB               http://www.enterprisedb.com
? GNUmakefile
? config.log
? config.status
? cscope.out
? src/Makefile.global
? src/backend/postgres
? src/backend/catalog/postgres.bki
? src/backend/catalog/postgres.description
? src/backend/catalog/postgres.shdescription
? src/backend/utils/mb/conversion_procs/conversion_create.sql
? src/backend/utils/mb/conversion_procs/ascii_and_mic/libascii_and_mic.so.0.0
? src/backend/utils/mb/conversion_procs/cyrillic_and_mic/libcyrillic_and_mic.so.0.0
? src/backend/utils/mb/conversion_procs/euc_cn_and_mic/libeuc_cn_and_mic.so.0.0
? src/backend/utils/mb/conversion_procs/euc_jis_2004_and_shift_jis_2004/libeuc_jis_2004_and_shift_jis_2004.so.0.0
? src/backend/utils/mb/conversion_procs/euc_jp_and_sjis/libeuc_jp_and_sjis.so.0.0
? src/backend/utils/mb/conversion_procs/euc_kr_and_mic/libeuc_kr_and_mic.so.0.0
? src/backend/utils/mb/conversion_procs/euc_tw_and_big5/libeuc_tw_and_big5.so.0.0
? src/backend/utils/mb/conversion_procs/latin2_and_win1250/liblatin2_and_win1250.so.0.0
? src/backend/utils/mb/conversion_procs/latin_and_mic/liblatin_and_mic.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_ascii/libutf8_and_ascii.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_big5/libutf8_and_big5.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_cyrillic/libutf8_and_cyrillic.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_euc_cn/libutf8_and_euc_cn.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_euc_jis_2004/libutf8_and_euc_jis_2004.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_euc_jp/libutf8_and_euc_jp.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_euc_kr/libutf8_and_euc_kr.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_euc_tw/libutf8_and_euc_tw.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_gb18030/libutf8_and_gb18030.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_gbk/libutf8_and_gbk.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_iso8859/libutf8_and_iso8859.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_iso8859_1/libutf8_and_iso8859_1.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_johab/libutf8_and_johab.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_shift_jis_2004/libutf8_and_shift_jis_2004.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_sjis/libutf8_and_sjis.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_uhc/libutf8_and_uhc.so.0.0
? src/backend/utils/mb/conversion_procs/utf8_and_win/libutf8_and_win.so.0.0
? src/bin/initdb/initdb
? src/bin/ipcclean/ipcclean
? src/bin/pg_config/pg_config
? src/bin/pg_controldata/pg_controldata
? src/bin/pg_ctl/pg_ctl
? src/bin/pg_dump/pg_dump
? src/bin/pg_dump/pg_dumpall
? src/bin/pg_dump/pg_restore
? src/bin/pg_resetxlog/pg_resetxlog
? src/bin/psql/psql
? src/bin/scripts/clusterdb
? src/bin/scripts/createdb
? src/bin/scripts/createlang
? src/bin/scripts/createuser
? src/bin/scripts/dropdb
? src/bin/scripts/droplang
? src/bin/scripts/dropuser
? src/bin/scripts/reindexdb
? src/bin/scripts/vacuumdb
? src/include/pg_config.h
? src/include/stamp-h
? src/interfaces/ecpg/compatlib/libecpg_compat.so.2.3
? src/interfaces/ecpg/ecpglib/libecpg.so.5.3
? src/interfaces/ecpg/include/ecpg_config.h
? src/interfaces/ecpg/pgtypeslib/libpgtypes.so.2.3
? src/interfaces/ecpg/preproc/.pgc.c.swp
? src/interfaces/ecpg/preproc/ecpg
? src/interfaces/libpq/exports.list
? src/interfaces/libpq/libpq.so.5.1
? src/pl/plpgsql/src/libplpgsql.so.1.0
? src/port/pg_config_paths.h
? src/test/regress/libregress.so.0.0
? src/test/regress/pg_regress
? src/test/regress/results
? src/test/regress/testtablespace
? src/test/regress/expected/constraints.out
? src/test/regress/expected/copy.out
? src/test/regress/expected/create_function_1.out
? src/test/regress/expected/create_function_2.out
? src/test/regress/expected/largeobject.out
? src/test/regress/expected/largeobject_1.out
? src/test/regress/expected/misc.out
? src/test/regress/expected/tablespace.out
? src/test/regress/sql/constraints.sql
? src/test/regress/sql/copy.sql
? src/test/regress/sql/create_function_1.sql
? src/test/regress/sql/create_function_2.sql
? src/test/regress/sql/largeobject.sql
? src/test/regress/sql/misc.sql
? src/test/regress/sql/tablespace.sql
? src/timezone/zic
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.87
diff -c -r1.87 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c	27 Feb 2007 01:11:25 -0000	1.87
--- src/backend/executor/nodeSubplan.c	11 Apr 2007 08:04:32 -0000
***************
*** 322,329 ****
  			continue;
  		}
  
! 		/* cannot allow multiple input tuples for ROWCOMPARE sublink either */
! 		if (subLinkType == ROWCOMPARE_SUBLINK && found)
  			ereport(ERROR,
  					(errcode(ERRCODE_CARDINALITY_VIOLATION),
  					 errmsg("more than one row returned by a subquery used as an expression")));
--- 322,330 ----
  			continue;
  		}
  
! 		/* cannot allow multiple input tuples for ROWCOMPARE/ROWEXPR sublink either */
! 		if ((subLinkType == ROWCOMPARE_SUBLINK || subLinkType == ROWEXPR_SUBLINK) 
! 			&& found)
  			ereport(ERROR,
  					(errcode(ERRCODE_CARDINALITY_VIOLATION),
  					 errmsg("more than one row returned by a subquery used as an expression")));
***************
*** 376,382 ****
  		}
  		else
  		{
! 			/* must be ROWCOMPARE_SUBLINK */
  			result = rowresult;
  			*isNull = rownull;
  		}
--- 377,383 ----
  		}
  		else
  		{
! 			/* must be ROWCOMPARE_SUBLINK  or ROWEXPR_SUBLINK */
  			result = rowresult;
  			*isNull = rownull;
  		}
***************
*** 391,397 ****
  		 */
  		if (subLinkType == EXPR_SUBLINK ||
  			subLinkType == ARRAY_SUBLINK ||
! 			subLinkType == ROWCOMPARE_SUBLINK)
  		{
  			result = (Datum) 0;
  			*isNull = true;
--- 392,399 ----
  		 */
  		if (subLinkType == EXPR_SUBLINK ||
  			subLinkType == ARRAY_SUBLINK ||
! 			subLinkType == ROWCOMPARE_SUBLINK ||
! 			subLinkType == ROWEXPR_SUBLINK)
  		{
  			result = (Datum) 0;
  			*isNull = true;
***************
*** 937,942 ****
--- 939,945 ----
  
  		if (found &&
  			(subLinkType == EXPR_SUBLINK ||
+ 			 subLinkType == ROWEXPR_SUBLINK ||
  			 subLinkType == ROWCOMPARE_SUBLINK))
  			ereport(ERROR,
  					(errcode(ERRCODE_CARDINALITY_VIOLATION),
Index: src/backend/optimizer/plan/planner.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/optimizer/plan/planner.c,v
retrieving revision 1.216
diff -c -r1.216 planner.c
*** src/backend/optimizer/plan/planner.c	27 Feb 2007 01:11:25 -0000	1.216
--- src/backend/optimizer/plan/planner.c	11 Apr 2007 08:04:32 -0000
***************
*** 72,77 ****
--- 72,78 ----
  						List *sub_tlist,
  						AttrNumber *groupColIdx);
  static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
+ static void preprocess_subselects(PlannerInfo *root, Node *expr);
  
  
  /*****************************************************************************
***************
*** 273,278 ****
--- 274,284 ----
  	expand_inherited_tables(root);
  
  	/*
+ 	 * preprocess subselects if present in the Query
+ 	 */
+ 	preprocess_subselects(root, (Node *)parse->subSelects);
+ 
+ 	/*
  	 * Set hasHavingQual to remember if HAVING clause is present.  Needed
  	 * because preprocess_expression will reduce a constant-true condition to
  	 * an empty qual list ... but "HAVING TRUE" is not a semantic no-op.
***************
*** 1752,1754 ****
--- 1758,1776 ----
  		elog(ERROR, "resjunk output columns are not implemented");
  	return new_tlist;
  }
+ 
+ /*
+  * preprocess_subselects
+  */
+ static void
+ preprocess_subselects(PlannerInfo *root, Node *expr)
+ {
+ 	/*
+ 	 * Fall out quickly if expression is empty.
+ 	 */
+ 	if (expr == NULL)
+ 		return;
+ 	SS_process_sublinks(root, expr, false);
+ 
+ 	return;
+ }
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.122
diff -c -r1.122 subselect.c
*** src/backend/optimizer/plan/subselect.c	27 Feb 2007 01:11:25 -0000	1.122
--- src/backend/optimizer/plan/subselect.c	11 Apr 2007 08:04:32 -0000
***************
*** 68,73 ****
--- 68,79 ----
  			  Bitmapset *outer_params,
  			  Bitmapset *valid_params);
  static bool finalize_primnode(Node *node, finalize_primnode_context *context);
+ static Node *convert_rowexpr(PlannerInfo *root,
+ 				 Node *testexpr,
+ 				 int rtindex,
+ 				 List **righthandIds);
+ static bool convert_rowexpr_walker(Node *node,
+ 						 convert_testexpr_context *context);
  
  
  /*
***************
*** 385,390 ****
--- 391,406 ----
  		 * plan's expression tree; it is not kept in the initplan node.
  		 */
  	}
+ 	else if (splan->parParam == NIL && slink->subLinkType == ROWEXPR_SUBLINK)
+ 	{
+ 		/* Adjust the Params */
+ 		result = convert_rowexpr(root,
+ 								  slink->testexpr,
+ 								  0,
+ 								  &splan->paramIds);
+ 		splan->setParam = list_copy(splan->paramIds);
+ 		isInitPlan = true;
+ 	}
  	else
  	{
  		List	   *args;
***************
*** 1376,1378 ****
--- 1392,1459 ----
  
  	return prm;
  }
+ 
+ /*
+  * convert_rowexpr:
+  * 	Use a walker to do an in-situ change to the PARAM entries involved in a
+  * 	ROWEXPR_SUBLINK subquery 
+  */
+ static Node *
+ convert_rowexpr(PlannerInfo *root,
+ 				 Node *testexpr,
+ 				 int rtindex,
+ 				 List **righthandIds)
+ {
+ 	convert_testexpr_context context;
+ 
+ 	context.root = root;
+ 	context.rtindex = rtindex;
+ 	context.righthandIds = NIL;
+ 	convert_rowexpr_walker(testexpr, &context);
+ 	*righthandIds = context.righthandIds;
+ 	return testexpr;
+ }
+ 
+ static bool 
+ convert_rowexpr_walker(Node *node,
+ 						 convert_testexpr_context *context)
+ {
+ 	if (node == NULL)
+ 		return false;
+ 
+ 	if (IsA(node, Param))
+ 	{
+ 		Param	   *param = (Param *) node;
+ 
+ 		if (param->paramkind == PARAM_SUBLINK)
+ 		{
+ 			PlannerParamItem *pitem;
+ 
+ 			/*
+ 			 * We expect to encounter the Params in column-number sequence. We
+ 			 * could handle non-sequential order if necessary, but for now
+ 			 * there's no need.  (This is also a useful cross-check that we
+ 			 * aren't finding any unexpected Params.)
+ 			 */
+ 			if (param->paramid != list_length(context->righthandIds) + 1)
+ 				elog(ERROR, "unexpected PARAM_SUBLINK ID: %d", param->paramid);
+ 
+ 			/* Modify the Param node in-situ representing the subplan's result */
+ 			param->paramkind = PARAM_EXEC;
+ 			param->paramid = list_length(context->root->glob->paramlist);
+ 
+ 			pitem = makeNode(PlannerParamItem);
+ 			pitem->item = (Node *) param;
+ 			pitem->abslevel = context->root->query_level;
+ 
+ 			context->root->glob->paramlist = 
+ 				lappend(context->root->glob->paramlist, pitem);
+ 
+ 			context->righthandIds = lappend_int(context->righthandIds,
+ 												param->paramid);
+ 		}
+ 	}
+ 	return expression_tree_walker((Node *)node,
+ 								   convert_rowexpr_walker,
+ 								   (void *) context);
+ }
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.362
diff -c -r1.362 analyze.c
*** src/backend/parser/analyze.c	13 Mar 2007 00:33:41 -0000	1.362
--- src/backend/parser/analyze.c	11 Apr 2007 08:04:32 -0000
***************
*** 110,116 ****
  static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
  static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
  					List **extras_before, List **extras_after);
! static List *transformInsertRow(ParseState *pstate, List *exprlist,
  				   List *stmtcols, List *icolumns, List *attrnos);
  static List *transformReturningList(ParseState *pstate, List *returningList);
  static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
--- 110,116 ----
  static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
  static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
  					List **extras_before, List **extras_after);
! static List *transformRow(ParseState *pstate, List *exprlist,
  				   List *stmtcols, List *icolumns, List *attrnos);
  static List *transformReturningList(ParseState *pstate, List *returningList);
  static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
***************
*** 497,503 ****
  										 false, false, ACL_INSERT);
  
  	/* Validate stmt->cols list, or build default list if no list given */
! 	icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
  	Assert(list_length(icolumns) == list_length(attrnos));
  
  	/*
--- 497,503 ----
  										 false, false, ACL_INSERT);
  
  	/* Validate stmt->cols list, or build default list if no list given */
! 	icolumns = checkTargets(pstate, stmt->cols, &attrnos);
  	Assert(list_length(icolumns) == list_length(attrnos));
  
  	/*
***************
*** 603,609 ****
  		}
  
  		/* Prepare row for assignment to target table */
! 		exprList = transformInsertRow(pstate, exprList,
  									  stmt->cols,
  									  icolumns, attrnos);
  	}
--- 603,609 ----
  		}
  
  		/* Prepare row for assignment to target table */
! 		exprList = transformRow(pstate, exprList,
  									  stmt->cols,
  									  icolumns, attrnos);
  	}
***************
*** 643,649 ****
  			}
  
  			/* Prepare row for assignment to target table */
! 			sublist = transformInsertRow(pstate, sublist,
  										 stmt->cols,
  										 icolumns, attrnos);
  
--- 643,649 ----
  			}
  
  			/* Prepare row for assignment to target table */
! 			sublist = transformRow(pstate, sublist,
  										 stmt->cols,
  										 icolumns, attrnos);
  
***************
*** 710,716 ****
  										   (List *) linitial(valuesLists));
  
  		/* Prepare row for assignment to target table */
! 		exprList = transformInsertRow(pstate, exprList,
  									  stmt->cols,
  									  icolumns, attrnos);
  	}
--- 710,716 ----
  										   (List *) linitial(valuesLists));
  
  		/* Prepare row for assignment to target table */
! 		exprList = transformRow(pstate, exprList,
  									  stmt->cols,
  									  icolumns, attrnos);
  	}
***************
*** 771,783 ****
  }
  
  /*
!  * Prepare an INSERT row for assignment to the target table.
   *
   * The row might be either a VALUES row, or variables referencing a
   * sub-SELECT output.
   */
  static List *
! transformInsertRow(ParseState *pstate, List *exprlist,
  				   List *stmtcols, List *icolumns, List *attrnos)
  {
  	List	   *result;
--- 771,783 ----
  }
  
  /*
!  * Prepare an INSERT/UPDATE row for assignment to the target table.
   *
   * The row might be either a VALUES row, or variables referencing a
   * sub-SELECT output.
   */
  static List *
! transformRow(ParseState *pstate, List *exprlist,
  				   List *stmtcols, List *icolumns, List *attrnos)
  {
  	List	   *result;
***************
*** 795,806 ****
  	if (list_length(exprlist) > list_length(icolumns))
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("INSERT has more expressions than target columns")));
  	if (stmtcols != NIL &&
  		list_length(exprlist) < list_length(icolumns))
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("INSERT has more target columns than expressions")));
  
  	/*
  	 * Prepare columns for assignment to target table.
--- 795,806 ----
  	if (list_length(exprlist) > list_length(icolumns))
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("INSERT/UPDATE has more expressions than target columns")));
  	if (stmtcols != NIL &&
  		list_length(exprlist) < list_length(icolumns))
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("INSERT/UPDATE has more target columns than expressions")));
  
  	/*
  	 * Prepare columns for assignment to target table.
***************
*** 2784,2790 ****
--- 2784,2802 ----
  	Node	   *qual;
  	ListCell   *origTargetList;
  	ListCell   *tl;
+  	ListCell   *lc, *lcell;
+  	List	   *exprList = NIL;
+  	List	   *icolumns;
+  	List	   *attrnos;
+  	List	   *otherColsList = NIL;
+  	List	   *selectColsList = NIL;
+  	List	   *selectQryList;
+  	List	   *subselectQryList = NIL;
+  	ListCell   *icols;
+  	ListCell   *attnos;
+  	SubLink		*subselect;
  
+ 	qry->subSelects = NIL;
  	qry->commandType = CMD_UPDATE;
  	pstate->p_is_update = true;
  
***************
*** 2792,2797 ****
--- 2804,2890 ----
  								  interpretInhOption(stmt->relation->inhOpt),
  										 true,
  										 ACL_UPDATE);
+  	/*
+  	 * check if the targetList contains subselects and if so process each of
+  	 * them
+  	 */
+  	qry->targetList = NIL;
+ 	subselectQryList = NIL;
+  	foreach(lcell, stmt->targetList)
+  	{
+  		Query	   *selectQuery;
+  		
+  		if (!IsA(lfirst(lcell), SubLink))
+  		{
+  			otherColsList = lappend(otherColsList, lfirst(lcell));
+  			continue;
+  		}
+  
+  		subselect = (SubLink *)lfirst(lcell);
+  
+  		/* Validate subselect->cols list */
+  		icolumns = checkTargets(pstate, subselect->cols, &attrnos);
+  		Assert(list_length(icolumns) == list_length(attrnos));
+  
+  		/*
+  		 * Maintain sub select based target cols in another list
+  		 */
+  		selectColsList = list_concat(selectColsList, subselect->cols);  
+  
+  		/*
+  		 * Process the SELECT subquery.
+  		 */
+  
+  		transformExpr(pstate, (Node *)subselect);
+  		selectQuery = (Query *)subselect->subselect;
+  
+  		Assert(IsA(selectQuery, Query));
+  		Assert(selectQuery->commandType == CMD_SELECT);
+  		if (selectQuery->into)
+  			ereport(ERROR,
+  					(errcode(ERRCODE_SYNTAX_ERROR),
+  					 errmsg("UPDATE ... SELECT cannot specify INTO")));
+ 
+  		/* Prepare row for assignment to target table */ 
+  		exprList = transformRow(pstate, (List *)subselect->testexpr,
+  									  subselect->cols,
+  									  icolumns, attrnos);
+  		/*
+  		 * Generate query's target list using the computed list of expressions.
+  		 */
+ 		selectQryList = NIL;
+  		icols = list_head(icolumns);
+  		attnos = list_head(attrnos);
+  		foreach(lc, exprList)
+  		{
+  			Expr	   *expr = (Expr *) lfirst(lc);
+  			ResTarget  *col;
+  			TargetEntry *tle;
+  
+  			col = (ResTarget *) lfirst(icols);
+  			Assert(IsA(col, ResTarget));
+  
+  			tle = makeTargetEntry(expr,
+  								  (AttrNumber) lfirst_int(attnos),
+  								  col->name,
+  								  false);
+  			selectQryList = lappend(selectQryList, tle);
+  
+  			icols = lnext(icols);
+  			attnos = lnext(attnos);
+  		}
+ 		subselect->testexpr = (Node *)selectQryList;
+ 		/*
+ 		 * Need to use list_copy otherwise targets of multiple subselects get
+ 		 * chained to one another
+ 		 */
+ 		subselectQryList = list_concat(subselectQryList, list_copy(selectQryList));
+ 
+ 		/*
+ 		 * Add this sublink to the Query for further processing by the planner
+ 		 */
+ 		qry->subSelects = lappend(qry->subSelects, subselect);
+  	}
  
  	/*
  	 * the FROM clause is non-standard SQL syntax. We used to be able to do
***************
*** 2799,2806 ****
  	 */
  	transformFromClause(pstate, stmt->fromClause);
  
! 	qry->targetList = transformTargetList(pstate, stmt->targetList);
! 
  	qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
  
  	qry->returningList = transformReturningList(pstate, stmt->returningList);
--- 2892,2901 ----
  	 */
  	transformFromClause(pstate, stmt->fromClause);
  
!  	
!  	if (otherColsList)
!  		qry->targetList = transformTargetList(pstate, otherColsList);
!  
  	qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
  
  	qry->returningList = transformReturningList(pstate, stmt->returningList);
***************
*** 2829,2836 ****
  	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)
  	{
--- 2924,2935 ----
  	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. 
! 	 * The subselect based targets have already been processed, process the
! 	 * other remaining entries here
! 	 */
!  	origTargetList = list_head(otherColsList);
  
  	foreach(tl, qry->targetList)
  	{
***************
*** 2875,2880 ****
--- 2974,2984 ----
  	if (origTargetList != NULL)
  		elog(ERROR, "UPDATE target count mismatch --- internal error");
  
+ 	/*
+ 	 * subselect based target entries need to be made a part of the query's
+ 	 * targetList here too
+ 	 */
+  	qry->targetList = list_concat(qry->targetList, subselectQryList);
  	return qry;
  }
  
Index: src/backend/parser/gram.y
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.587
diff -c -r2.587 gram.y
*** src/backend/parser/gram.y	8 Apr 2007 00:26:34 -0000	2.587
--- src/backend/parser/gram.y	11 Apr 2007 08:04:32 -0000
***************
*** 5774,5779 ****
--- 5774,5789 ----
  
  					$$ = $2;
  				}
+ 			| '(' set_target_list ')' '=' select_with_parens
+ 				{
+ 					SubLink *n = makeNode(SubLink);
+ 					n->subLinkType = ROWEXPR_SUBLINK;
+ 					n->testexpr = NULL;
+ 					n->operName = NIL;
+ 					n->subselect = $5;
+ 					n->cols = $2;
+ 					$$ = list_make1((Node *)n);
+ 				}
  		;
  
  set_target:
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/parser/parse_expr.c,v
retrieving revision 1.216
diff -c -r1.216 parse_expr.c
*** src/backend/parser/parse_expr.c	2 Apr 2007 03:49:39 -0000	1.216
--- src/backend/parser/parse_expr.c	11 Apr 2007 08:04:32 -0000
***************
*** 1159,1164 ****
--- 1159,1204 ----
  		sublink->testexpr = NULL;
  		sublink->operName = NIL;
  	}
+ 	else if (sublink->subLinkType == ROWEXPR_SUBLINK)
+ 	{
+ 		List	   *left_list;
+ 		List	   *right_list;
+ 		ListCell   *l;
+ 
+ 		left_list = sublink->cols;
+ 		Assert(left_list != NULL);
+ 		/*
+ 		 * Build a list of PARAM_SUBLINK nodes representing the output columns
+ 		 * of the subquery.
+ 		 */
+ 		right_list = NIL;
+ 		foreach(l, qtree->targetList)
+ 		{
+ 			TargetEntry *tent = (TargetEntry *) lfirst(l);
+ 			Param	   *param;
+ 
+ 			if (tent->resjunk)
+ 				continue;
+ 
+ 			param = makeNode(Param);
+ 			param->paramkind = PARAM_SUBLINK;
+ 			param->paramid = tent->resno;
+ 			param->paramtype = exprType((Node *) tent->expr);
+ 			param->paramtypmod = exprTypmod((Node *) tent->expr);
+ 
+ 			right_list = lappend(right_list, param);
+ 		}
+ 
+ 		if (list_length(left_list) < list_length(right_list))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					 errmsg("subquery has too many columns")));
+ 		if (list_length(left_list) > list_length(right_list))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					 errmsg("subquery has too few columns")));
+ 		sublink->testexpr = (Node *)right_list;
+ 	}
  	else
  	{
  		/* ALL, ANY, or ROWCOMPARE: generate row-comparing expression */
Index: src/backend/parser/parse_target.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/parser/parse_target.c,v
retrieving revision 1.154
diff -c -r1.154 parse_target.c
*** src/backend/parser/parse_target.c	3 Feb 2007 14:06:54 -0000	1.154
--- src/backend/parser/parse_target.c	11 Apr 2007 08:04:33 -0000
***************
*** 706,725 ****
  
  
  /*
!  * checkInsertTargets -
!  *	  generate a list of INSERT column targets if not supplied, or
   *	  test supplied column names to make sure they are in target table.
   *	  Also return an integer list of the columns' attribute numbers.
   */
  List *
! checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
  {
  	*attrnos = NIL;
  
  	if (cols == NIL)
  	{
  		/*
! 		 * Generate default column list for INSERT.
  		 */
  		Form_pg_attribute *attr = pstate->p_target_relation->rd_att->attrs;
  		int			numcol = pstate->p_target_relation->rd_rel->relnatts;
--- 706,725 ----
  
  
  /*
!  * checkTargets -
!  *	  generate a list of INSERT/UPDATE column targets if not supplied, or
   *	  test supplied column names to make sure they are in target table.
   *	  Also return an integer list of the columns' attribute numbers.
   */
  List *
! checkTargets(ParseState *pstate, List *cols, List **attrnos)
  {
  	*attrnos = NIL;
  
  	if (cols == NIL)
  	{
  		/*
! 		 * Generate default column list for INSERT/UPDATE.
  		 */
  		Form_pg_attribute *attr = pstate->p_target_relation->rd_att->attrs;
  		int			numcol = pstate->p_target_relation->rd_rel->relnatts;
***************
*** 744,750 ****
  	else
  	{
  		/*
! 		 * Do initial validation of user-supplied INSERT column list.
  		 */
  		Bitmapset  *wholecols = NULL;
  		Bitmapset  *partialcols = NULL;
--- 744,750 ----
  	else
  	{
  		/*
! 		 * Do initial validation of user-supplied INSERT/UPDATE column list.
  		 */
  		Bitmapset  *wholecols = NULL;
  		Bitmapset  *partialcols = NULL;
***************
*** 768,774 ****
  
  			/*
  			 * Check for duplicates, but only of whole columns --- we allow
! 			 * INSERT INTO foo (col.subcol1, col.subcol2)
  			 */
  			if (col->indirection == NIL)
  			{
--- 768,775 ----
  
  			/*
  			 * Check for duplicates, but only of whole columns --- we allow
! 			 * INSERT INTO foo (col.subcol1, col.subcol2). 
! 			 * Above applies for UPDATEs too
  			 */
  			if (col->indirection == NIL)
  			{
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.344
diff -c -r1.344 parsenodes.h
*** src/include/nodes/parsenodes.h	2 Apr 2007 03:49:41 -0000	1.344
--- src/include/nodes/parsenodes.h	11 Apr 2007 08:04:33 -0000
***************
*** 129,134 ****
--- 129,135 ----
  
  	Node	   *setOperations;	/* set-operation tree if this is top level of
  								 * a UNION/INTERSECT/EXCEPT query */
+ 	List	   *subSelects;		/* a list of subselects used in UPDATEs */
  } Query;
  
  
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/nodes/primnodes.h,v
retrieving revision 1.129
diff -c -r1.129 primnodes.h
*** src/include/nodes/primnodes.h	27 Mar 2007 23:21:12 -0000	1.129
--- src/include/nodes/primnodes.h	11 Apr 2007 08:04:33 -0000
***************
*** 377,382 ****
--- 377,383 ----
   *	ANY_SUBLINK			(lefthand) op ANY (SELECT ...)
   *	ROWCOMPARE_SUBLINK	(lefthand) op (SELECT ...)
   *	EXPR_SUBLINK		(SELECT with single targetlist item ...)
+  *	ROWEXPR_SUBLINK		(SELECT with multiple targetlist items ...)
   *	ARRAY_SUBLINK		ARRAY(SELECT with single targetlist item ...)
   * For ALL, ANY, and ROWCOMPARE, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  ROWCOMPARE will *always* have
***************
*** 384,392 ****
   * then the parser will create an EXPR_SUBLINK instead (and any operator
   * above the subselect will be represented separately).  Note that both
   * ROWCOMPARE and EXPR require the subselect to deliver only one row.
!  * ALL, ANY, and ROWCOMPARE require the combining operators to deliver boolean
!  * results.  ALL and ANY combine the per-row results using AND and OR
!  * semantics respectively.
   * ARRAY requires just one target column, and creates an array of the target
   * column's type using one or more rows resulting from the subselect.
   *
--- 385,394 ----
   * then the parser will create an EXPR_SUBLINK instead (and any operator
   * above the subselect will be represented separately).  Note that both
   * ROWCOMPARE and EXPR require the subselect to deliver only one row.
!  * For subselects with multiple targets, the parser will create a
!  * ROWEXPR sublink. ALL, ANY, and ROWCOMPARE require the combining operators 
!  * to deliver boolean results.  ALL and ANY combine the per-row results using
!  * AND and OR semantics respectively.
   * ARRAY requires just one target column, and creates an array of the target
   * column's type using one or more rows resulting from the subselect.
   *
***************
*** 412,417 ****
--- 414,420 ----
  	ANY_SUBLINK,
  	ROWCOMPARE_SUBLINK,
  	EXPR_SUBLINK,
+ 	ROWEXPR_SUBLINK,
  	ARRAY_SUBLINK
  } SubLinkType;
  
***************
*** 422,427 ****
--- 425,431 ----
  	SubLinkType subLinkType;	/* see above */
  	Node	   *testexpr;		/* outer-query test for ALL/ANY/ROWCOMPARE */
  	List	   *operName;		/* originally specified operator name */
+ 	List	   *cols;			/* names of the target columns for ROWEXPR_SUBLINK */
  	Node	   *subselect;		/* subselect as Query* or parsetree */
  } SubLink;
  
Index: src/include/parser/parse_target.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/parser/parse_target.h,v
retrieving revision 1.42
diff -c -r1.42 parse_target.h
*** src/include/parser/parse_target.h	5 Jan 2007 22:19:57 -0000	1.42
--- src/include/parser/parse_target.h	11 Apr 2007 08:04:33 -0000
***************
*** 32,38 ****
  					  char *colname, int attrno,
  					  List *indirection,
  					  int location);
! extern List *checkInsertTargets(ParseState *pstate, List *cols,
  				   List **attrnos);
  extern TupleDesc expandRecordVariable(ParseState *pstate, Var *var,
  					 int levelsup);
--- 32,38 ----
  					  char *colname, int attrno,
  					  List *indirection,
  					  int location);
! extern List *checkTargets(ParseState *pstate, List *cols,
  				   List **attrnos);
  extern TupleDesc expandRecordVariable(ParseState *pstate, Var *var,
  					 int levelsup);
Index: src/test/regress/expected/alter_table.out
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/test/regress/expected/alter_table.out,v
retrieving revision 1.101
diff -c -r1.101 alter_table.out
*** src/test/regress/expected/alter_table.out	14 Feb 2007 01:58:58 -0000	1.101
--- src/test/regress/expected/alter_table.out	11 Apr 2007 08:04:33 -0000
***************
*** 765,773 ****
                                        ^
  -- INSERTs
  insert into atacc1 values (10, 11, 12, 13);
! ERROR:  INSERT has more expressions than target columns
  insert into atacc1 values (default, 11, 12, 13);
! ERROR:  INSERT has more expressions than target columns
  insert into atacc1 values (11, 12, 13);
  insert into atacc1 (a) values (10);
  ERROR:  column "a" of relation "atacc1" does not exist
--- 765,773 ----
                                        ^
  -- INSERTs
  insert into atacc1 values (10, 11, 12, 13);
! ERROR:  INSERT/UPDATE has more expressions than target columns
  insert into atacc1 values (default, 11, 12, 13);
! ERROR:  INSERT/UPDATE has more expressions than target columns
  insert into atacc1 values (11, 12, 13);
  insert into atacc1 (a) values (10);
  ERROR:  column "a" of relation "atacc1" does not exist
Index: src/test/regress/expected/insert.out
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/test/regress/expected/insert.out,v
retrieving revision 1.8
diff -c -r1.8 insert.out
*** src/test/regress/expected/insert.out	3 Aug 2006 14:54:44 -0000	1.8
--- src/test/regress/expected/insert.out	11 Apr 2007 08:04:33 -0000
***************
*** 21,33 ****
  -- insert with similar expression / target_list values (all fail)
  --
  insert into inserttest (col1, col2, col3) values (DEFAULT, DEFAULT);
! ERROR:  INSERT has more target columns than expressions
  insert into inserttest (col1, col2, col3) values (1, 2);
! ERROR:  INSERT has more target columns than expressions
  insert into inserttest (col1) values (1, 2);
! ERROR:  INSERT has more expressions than target columns
  insert into inserttest (col1) values (DEFAULT, DEFAULT);
! ERROR:  INSERT has more expressions than target columns
  select * from inserttest;
   col1 | col2 |  col3   
  ------+------+---------
--- 21,33 ----
  -- insert with similar expression / target_list values (all fail)
  --
  insert into inserttest (col1, col2, col3) values (DEFAULT, DEFAULT);
! ERROR:  INSERT/UPDATE has more target columns than expressions
  insert into inserttest (col1, col2, col3) values (1, 2);
! ERROR:  INSERT/UPDATE has more target columns than expressions
  insert into inserttest (col1) values (1, 2);
! ERROR:  INSERT/UPDATE has more expressions than target columns
  insert into inserttest (col1) values (DEFAULT, DEFAULT);
! ERROR:  INSERT/UPDATE has more expressions than target columns
  select * from inserttest;
   col1 | col2 |  col3   
  ------+------+---------
Index: src/test/regress/expected/update.out
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/test/regress/expected/update.out,v
retrieving revision 1.5
diff -c -r1.5 update.out
*** src/test/regress/expected/update.out	3 Sep 2006 22:37:06 -0000	1.5
--- src/test/regress/expected/update.out	11 Apr 2007 08:04:33 -0000
***************
*** 74,85 ****
  -- fail, multi assignment to same column:
  UPDATE update_test SET (c,b) = ('car', a+b), b = a + 1 WHERE a = 10;
  ERROR:  multiple assignments to same column "b"
! -- XXX this should work, but doesn't yet:
! UPDATE update_test SET (a,b) = (select a,b FROM update_test where c = 'foo')
!   WHERE a = 10;
! ERROR:  syntax error at or near "select"
! LINE 1: UPDATE update_test SET (a,b) = (select a,b FROM update_test ...
!                                         ^
  -- if an alias for the target table is specified, don't allow references
  -- to the original table name
  BEGIN;
--- 74,131 ----
  -- fail, multi assignment to same column:
  UPDATE update_test SET (c,b) = ('car', a+b), b = a + 1 WHERE a = 10;
  ERROR:  multiple assignments to same column "b"
! INSERT INTO update_test VALUES (5, 10, 'foo');
! -- update using subselect:
! UPDATE update_test SET (a,b) = (select a+5,b FROM update_test where c = 'foo')
!   WHERE a = 5;
! SELECT * FROM update_test;
!   a  | b  |  c  
! -----+----+-----
!  100 | 20 | 
!   11 | 41 | car
!   10 | 10 | foo
! (3 rows)
! 
! -- fail update when subselect returns multiple rows:
! UPDATE update_test SET (a,b) = (select a,b FROM update_test);
! ERROR:  more than one row returned by a subquery used as an expression
! -- combination of multiple subselects in UPDATEs
! UPDATE update_test SET a= (select a FROM update_test where c = 'foo'), 
! 	b = (select b+100 FROM update_test where c = 'foo') WHERE a = 10;
! SELECT * FROM update_test;
!   a  |  b  |  c  
! -----+-----+-----
!  100 |  20 | 
!   11 |  41 | car
!   10 | 110 | foo
! (3 rows)
! 
! -- combination of normal SET with subselects
! UPDATE update_test SET a= (select a+5 FROM update_test where c = 'foo'), 
! 	b = 100 WHERE a = 10;
! SELECT * FROM update_test;
!   a  |  b  |  c  
! -----+-----+-----
!  100 |  20 | 
!   11 |  41 | car
!   15 | 100 | foo
! (3 rows)
! 
! -- fail, multi assignment to same column when subselects are involved
! UPDATE update_test SET (c,b) = ('car', a+b), 
! 	b = (select b from update_test where c = 'foo') WHERE a = 10;
! ERROR:  multiple assignments to same column "b"
! -- update using subselect, subquery should return NULLs if there are no matches
! UPDATE update_test SET (a,b) = (select a,b FROM update_test where c = 'bar')
!   WHERE a = 15;
! SELECT * FROM update_test;
!   a  | b  |  c  
! -----+----+-----
!  100 | 20 | 
!   11 | 41 | car
!      |    | foo
! (3 rows)
! 
  -- if an alias for the target table is specified, don't allow references
  -- to the original table name
  BEGIN;
Index: src/test/regress/sql/update.sql
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/test/regress/sql/update.sql,v
retrieving revision 1.5
diff -c -r1.5 update.sql
*** src/test/regress/sql/update.sql	3 Sep 2006 22:37:06 -0000	1.5
--- src/test/regress/sql/update.sql	11 Apr 2007 08:04:33 -0000
***************
*** 46,54 ****
  -- fail, multi assignment to same column:
  UPDATE update_test SET (c,b) = ('car', a+b), b = a + 1 WHERE a = 10;
  
! -- XXX this should work, but doesn't yet:
! UPDATE update_test SET (a,b) = (select a,b FROM update_test where c = 'foo')
!   WHERE a = 10;
  
  -- if an alias for the target table is specified, don't allow references
  -- to the original table name
--- 46,78 ----
  -- fail, multi assignment to same column:
  UPDATE update_test SET (c,b) = ('car', a+b), b = a + 1 WHERE a = 10;
  
! INSERT INTO update_test VALUES (5, 10, 'foo');
! -- update using subselect:
! UPDATE update_test SET (a,b) = (select a+5,b FROM update_test where c = 'foo')
!   WHERE a = 5;
! SELECT * FROM update_test;
! 
! -- fail update when subselect returns multiple rows:
! UPDATE update_test SET (a,b) = (select a,b FROM update_test);
! 
! -- combination of multiple subselects in UPDATEs
! UPDATE update_test SET a= (select a FROM update_test where c = 'foo'), 
! 	b = (select b+100 FROM update_test where c = 'foo') WHERE a = 10;
! SELECT * FROM update_test;
! 
! -- combination of normal SET with subselects
! UPDATE update_test SET a= (select a+5 FROM update_test where c = 'foo'), 
! 	b = 100 WHERE a = 10;
! SELECT * FROM update_test;
! 
! -- fail, multi assignment to same column when subselects are involved
! UPDATE update_test SET (c,b) = ('car', a+b), 
! 	b = (select b from update_test where c = 'foo') WHERE a = 10;
! 
! -- update using subselect, subquery should return NULLs if there are no matches
! UPDATE update_test SET (a,b) = (select a,b FROM update_test where c = 'bar')
!   WHERE a = 15;
! SELECT * FROM update_test;
  
  -- if an alias for the target table is specified, don't allow references
  -- to the original table name
---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?

               http://archives.postgresql.org

Reply via email to