From 0f059e1d131584cec380962d3166eab78da1b35c Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 4 Jun 2004 00:07:52 +0000
Subject: [PATCH] Allow plpgsql to pass composite-type arguments (ie, whole-row
 variables) into SQL expressions.  At present this only works usefully for
 variables of named rowtypes, not RECORD variables, since the SQL parser can't
 infer anything about datatypes from a RECORD Param.  Still, it's a step
 forward.

---
 src/pl/plpgsql/src/gram.y    | 30 +++++++---------
 src/pl/plpgsql/src/pl_exec.c | 69 ++++++++++++++++++++++++++++++------
 2 files changed, 71 insertions(+), 28 deletions(-)

diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 36427e2ceaa..7abc1e19b83 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -4,7 +4,7 @@
  *						  procedural language
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.54 2004/06/03 22:56:43 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.55 2004/06/04 00:07:52 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -1589,17 +1589,15 @@ read_sql_construct(int until,
 				break;
 
 			case T_ROW:
-				/* XXX make this work someday */
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("passing a whole row variable into a SQL command is not implemented")));
+				params[nparams] = yylval.row->rowno;
+				snprintf(buf, sizeof(buf), " $%d ", ++nparams);
+				plpgsql_dstring_append(&ds, buf);
 				break;
 
 			case T_RECORD:
-				/* XXX make this work someday */
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("passing a whole record variable into a SQL command is not implemented")));
+				params[nparams] = yylval.rec->recno;
+				snprintf(buf, sizeof(buf), " $%d ", ++nparams);
+				plpgsql_dstring_append(&ds, buf);
 				break;
 
 			default:
@@ -1810,17 +1808,15 @@ make_select_stmt(void)
 				break;
 
 			case T_ROW:
-				/* XXX make this work someday */
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("passing a whole row variable into a SQL command is not implemented")));
+				params[nparams] = yylval.row->rowno;
+				snprintf(buf, sizeof(buf), " $%d ", ++nparams);
+				plpgsql_dstring_append(&ds, buf);
 				break;
 
 			case T_RECORD:
-				/* XXX make this work someday */
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("passing a whole record variable into a SQL command is not implemented")));
+				params[nparams] = yylval.rec->recno;
+				snprintf(buf, sizeof(buf), " $%d ", ++nparams);
+				plpgsql_dstring_append(&ds, buf);
 				break;
 
 			default:
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 7fa1eecaca2..149c96ec7ba 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.102 2004/05/30 23:40:41 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.103 2004/06/04 00:07:52 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -2969,17 +2969,12 @@ exec_eval_datum(PLpgSQL_execstate * estate,
 				Datum *value,
 				bool *isnull)
 {
-	PLpgSQL_var *var;
-	PLpgSQL_rec *rec;
-	PLpgSQL_recfield *recfield;
-	PLpgSQL_trigarg *trigarg;
-	int			tgargno;
-	int			fno;
-
 	switch (datum->dtype)
 	{
 		case PLPGSQL_DTYPE_VAR:
-			var = (PLpgSQL_var *) datum;
+		{
+			PLpgSQL_var *var = (PLpgSQL_var *) datum;
+
 			*typeid = var->datatype->typoid;
 			*value = var->value;
 			*isnull = var->isnull;
@@ -2989,9 +2984,56 @@ exec_eval_datum(PLpgSQL_execstate * estate,
 						 errmsg("type of \"%s\" does not match that when preparing the plan",
 								var->refname)));
 			break;
+		}
+
+		case PLPGSQL_DTYPE_ROW:
+		{
+			PLpgSQL_row *row = (PLpgSQL_row *) datum;
+			HeapTuple	tup;
+
+			if (!row->rowtupdesc) /* should not happen */
+				elog(ERROR, "row variable has no tupdesc");
+			tup = make_tuple_from_row(estate, row, row->rowtupdesc);
+			if (tup == NULL)	/* should not happen */
+				elog(ERROR, "row not compatible with its own tupdesc");
+			*typeid = row->rowtupdesc->tdtypeid;
+			*value = HeapTupleGetDatum(tup);
+			*isnull = false;
+			if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("type of \"%s\" does not match that when preparing the plan",
+								row->refname)));
+			break;
+		}
+
+		case PLPGSQL_DTYPE_REC:
+		{
+			PLpgSQL_rec *rec = (PLpgSQL_rec *) datum;
+
+			if (!HeapTupleIsValid(rec->tup))
+				ereport(ERROR,
+						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						 errmsg("record \"%s\" is not assigned yet",
+								rec->refname),
+						 errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
+			*typeid = rec->tupdesc->tdtypeid;
+			*value = HeapTupleGetDatum(rec->tup);
+			*isnull = false;
+			if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("type of \"%s\" does not match that when preparing the plan",
+								rec->refname)));
+			break;
+		}
 
 		case PLPGSQL_DTYPE_RECFIELD:
-			recfield = (PLpgSQL_recfield *) datum;
+		{
+			PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) datum;
+			PLpgSQL_rec *rec;
+			int			fno;
+
 			rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
 			if (!HeapTupleIsValid(rec->tup))
 				ereport(ERROR,
@@ -3013,9 +3055,13 @@ exec_eval_datum(PLpgSQL_execstate * estate,
 						 errmsg("type of \"%s.%s\" does not match that when preparing the plan",
 								rec->refname, recfield->fieldname)));
 			break;
+		}
 
 		case PLPGSQL_DTYPE_TRIGARG:
-			trigarg = (PLpgSQL_trigarg *) datum;
+		{
+			PLpgSQL_trigarg *trigarg = (PLpgSQL_trigarg *) datum;
+			int			tgargno;
+
 			*typeid = TEXTOID;
 			tgargno = exec_eval_integer(estate, trigarg->argnum, isnull);
 			if (*isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
@@ -3034,6 +3080,7 @@ exec_eval_datum(PLpgSQL_execstate * estate,
 						 errmsg("type of tgargv[%d] does not match that when preparing the plan",
 								tgargno)));
 			break;
+		}
 
 		default:
 			elog(ERROR, "unrecognized dtype: %d", datum->dtype);
-- 
GitLab