From d21de3b12119e9b35c9f30fae0f053410a80b906 Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Thu, 13 Feb 2003 05:06:35 +0000
Subject: [PATCH] >
 ================================================================= > User
 interface proposal for multi-row function targetlist entries >
 ================================================================= > 1. Only
 one targetlist entry may return a set. > 2. Each targetlist item (other than
 the set returning one) is >    repeated for each item in the returned set. >

Having gotten no objections (actually, no response at all), I can only assume
no one had heartburn with this change. The attached patch covers the first of
the two proposals, i.e. restricting the target list to only one set returning
function.

It compiles cleanly, and passes all regression tests. If there are no
objections, please apply.

Any suggestions on where this should be documented (other than maybe sql-select)?

Thanks,

Joe

p.s. Here's what the previous example now looks like:
CREATE TABLE bar(f1 int, f2 text, f3 int);
INSERT INTO bar VALUES(1, 'Hello', 42);
INSERT INTO bar VALUES(2, 'Happy', 45);

CREATE TABLE foo(a int, b text);
INSERT INTO foo VALUES(42, 'World');
INSERT INTO foo VALUES(42, 'Everyone');
INSERT INTO foo VALUES(45, 'Birthday');
INSERT INTO foo VALUES(45, 'New Year');

CREATE TABLE foo2(a int, b text);
INSERT INTO foo2 VALUES(42, '!!!!');
INSERT INTO foo2 VALUES(42, '????');
INSERT INTO foo2 VALUES(42, '####');
INSERT INTO foo2 VALUES(45, '$$$$');

CREATE OR REPLACE FUNCTION getfoo(int) RETURNS SETOF text AS '
   SELECT b FROM foo WHERE a = $1
' language 'sql';

CREATE OR REPLACE FUNCTION getfoo2(int) RETURNS SETOF text AS '
   SELECT b FROM foo2 WHERE a = $1
' language 'sql';

regression=# SELECT f1, f2, getfoo(f3) AS f4 FROM bar;
  f1 |  f2   |    f4
----+-------+----------
   1 | Hello | World
   1 | Hello | Everyone
   2 | Happy | Birthday
   2 | Happy | New Year
(4 rows)

regression=# SELECT f1, f2, getfoo(f3) AS f4, getfoo2(f3) AS f5 FROM bar;
ERROR:  Only one target list entry may return a set result


Joe Conway
---
 src/backend/parser/parse_clause.c |  4 ++--
 src/backend/parser/parse_target.c | 24 ++++++++++++++++++++----
 src/include/parser/parse_target.h |  4 ++--
 3 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index ec468516cca..85af5359eaf 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.106 2003/02/10 04:44:46 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.107 2003/02/13 05:06:34 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1118,7 +1118,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
 	 * the end of the target list.	This target is given resjunk = TRUE so
 	 * that it will not be projected into the final tuple.
 	 */
-	target_result = transformTargetEntry(pstate, node, expr, NULL, true);
+	target_result = transformTargetEntry(pstate, node, expr, NULL, true, NULL);
 	lappend(tlist, target_result);
 
 	return target_result;
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 534b2e40978..0bc5e665d6f 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.95 2003/02/09 06:56:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.96 2003/02/13 05:06:35 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,13 +42,16 @@ static int	FigureColnameInternal(Node *node, char **name);
  * colname	the column name to be assigned, or NULL if none yet set.
  * resjunk	true if the target should be marked resjunk, ie, it is not
  *			wanted in the final projected tuple.
+ * retset	if non-NULL, and the entry is a function expression, pass back
+ *			expr->funcretset
  */
 TargetEntry *
 transformTargetEntry(ParseState *pstate,
 					 Node *node,
 					 Node *expr,
 					 char *colname,
-					 bool resjunk)
+					 bool resjunk,
+					 bool *retset)
 {
 	Oid			type_id;
 	int32		type_mod;
@@ -61,6 +64,9 @@ transformTargetEntry(ParseState *pstate,
 	if (IsA(expr, RangeVar))
 		elog(ERROR, "You can't use relation names alone in the target list, try relation.*.");
 
+	if (retset && IsA(expr, FuncExpr))
+		*retset = ((FuncExpr *) expr)->funcretset;
+
 	type_id = exprType(expr);
 	type_mod = exprTypmod(expr);
 
@@ -93,10 +99,12 @@ transformTargetEntry(ParseState *pstate,
 List *
 transformTargetList(ParseState *pstate, List *targetlist)
 {
+	bool		retset = false;
 	List	   *p_target = NIL;
 
 	while (targetlist != NIL)
 	{
+		bool		entry_retset = false;
 		ResTarget  *res = (ResTarget *) lfirst(targetlist);
 
 		if (IsA(res->val, ColumnRef))
@@ -173,7 +181,8 @@ transformTargetList(ParseState *pstate, List *targetlist)
 														res->val,
 														NULL,
 														res->name,
-														false));
+														false,
+														&entry_retset));
 			}
 		}
 		else if (IsA(res->val, InsertDefault))
@@ -194,9 +203,16 @@ transformTargetList(ParseState *pstate, List *targetlist)
 													res->val,
 													NULL,
 													res->name,
-													false));
+													false,
+													&entry_retset));
 		}
 
+		if (retset && entry_retset)
+			elog(ERROR, "Only one target list entry may return a set result");
+
+		if (entry_retset)
+			retset = true;
+
 		targetlist = lnext(targetlist);
 	}
 
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index b8c495484bf..3fa67cbf41f 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_target.h,v 1.27 2002/09/18 21:35:24 tgl Exp $
+ * $Id: parse_target.h,v 1.28 2003/02/13 05:06:35 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,7 @@
 extern List *transformTargetList(ParseState *pstate, List *targetlist);
 extern TargetEntry *transformTargetEntry(ParseState *pstate,
 					 Node *node, Node *expr,
-					 char *colname, bool resjunk);
+					 char *colname, bool resjunk, bool *retset);
 extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
 					  char *colname, int attrno,
 					  List *indirection);
-- 
GitLab