From 4089d2517560ec2265c42fcf7a786e04207f2787 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 27 Apr 2003 22:21:22 +0000
Subject: [PATCH] Fix plpgsql so that variables of composite types (rowtypes)
 can be declared without having to write %ROWTYPE.  If the declared type of a
 variable is a composite type, it'll be taken to be a row variable
 automatically.

---
 doc/src/sgml/plpgsql.sgml    | 20 +++++---
 src/pl/plpgsql/src/gram.y    | 70 +++++++++++++++++--------
 src/pl/plpgsql/src/pl_comp.c | 99 +++++++++++-------------------------
 src/pl/plpgsql/src/plpgsql.h | 16 +++---
 4 files changed, 99 insertions(+), 106 deletions(-)

diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 38306e100b2..2aa0c07ed1b 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.17 2003/04/07 01:29:25 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.18 2003/04/27 22:21:22 tgl Exp $
 -->
 
 <chapter id="plpgsql"> 
@@ -541,7 +541,8 @@ user_id users.user_id%TYPE;
      <title>Row Types</title>
 
 <synopsis>
-<replaceable>name</replaceable> <replaceable>tablename</replaceable><literal>%ROWTYPE</literal>;
+<replaceable>name</replaceable> <replaceable>table_name</replaceable><literal>%ROWTYPE</literal>;
+<replaceable>name</replaceable> <replaceable>composite_type_name</replaceable>;
 </synopsis>
 
    <para>
@@ -550,17 +551,20 @@ user_id users.user_id%TYPE;
     can hold a whole row of a <command>SELECT</> or <command>FOR</>
     query result, so long as that query's column set matches the
     declared type of the variable.
-    <replaceable>tablename</replaceable> must be an existing table or
-    view name in the database. The individual fields of the row value
+    The individual fields of the row value
     are accessed using the usual dot notation, for example
     <literal>rowvar.field</literal>.
    </para>
 
    <para>
-    Presently, a row variable can only be declared using the
-    <literal>%ROWTYPE</literal> notation; although one might expect a
-    bare table name to work as a type declaration, it won't be accepted
-    within <application>PL/pgSQL</application> functions.
+    A row variable can be declared to have the same type as the rows of
+    an existing table or view, by using the
+    <replaceable>table_name</replaceable><literal>%ROWTYPE</literal>
+    notation; or it can be declared by giving a composite type's name.
+    (Since every table has an associated datatype of the same name,
+    it actually does not matter in <productname>PostgreSQL</> whether you
+    write <literal>%ROWTYPE</literal> or not.  But the form with
+    <literal>%ROWTYPE</literal> is more portable.)
    </para>
 
    <para>
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 23dc8940290..dd15cf80a2f 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -4,7 +4,7 @@
  *						  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.41 2003/03/25 03:16:40 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.42 2003/04/27 22:21:22 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -307,36 +307,64 @@ decl_stmt		: '<' '<' opt_lblname '>' '>'
 
 decl_statement	: decl_varname decl_const decl_datatype decl_notnull decl_defval
 					{
-						PLpgSQL_var		*new;
+						if (!OidIsValid($3->typrelid))
+						{
+							/* Ordinary scalar datatype */
+							PLpgSQL_var		*var;
 
-						new = malloc(sizeof(PLpgSQL_var));
-						memset(new, 0, sizeof(PLpgSQL_var));
+							var = malloc(sizeof(PLpgSQL_var));
+							memset(var, 0, sizeof(PLpgSQL_var));
 
-						new->dtype		= PLPGSQL_DTYPE_VAR;
-						new->refname	= $1.name;
-						new->lineno		= $1.lineno;
+							var->dtype		= PLPGSQL_DTYPE_VAR;
+							var->refname	= $1.name;
+							var->lineno		= $1.lineno;
 
-						new->datatype	= $3;
-						new->isconst	= $2;
-						new->notnull	= $4;
-						new->default_val = $5;
+							var->datatype	= $3;
+							var->isconst	= $2;
+							var->notnull	= $4;
+							var->default_val = $5;
 
-						plpgsql_adddatum((PLpgSQL_datum *)new);
-						plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, new->varno,
-												$1.name);
+							plpgsql_adddatum((PLpgSQL_datum *)var);
+							plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR,
+											   var->varno,
+											   $1.name);
+						}
+						else
+						{
+							/* Composite type --- treat as rowtype */
+							PLpgSQL_row	   *row;
+
+							row = build_rowtype($3->typrelid);
+							row->dtype		= PLPGSQL_DTYPE_ROW;
+							row->refname	= $1.name;
+							row->lineno		= $1.lineno;
+
+							if ($2)
+								elog(ERROR, "Rowtype variable cannot be CONSTANT");
+							if ($4)
+								elog(ERROR, "Rowtype variable cannot be NOT NULL");
+							if ($5 != NULL)
+								elog(ERROR, "Default value for rowtype variable is not supported");
+
+							plpgsql_adddatum((PLpgSQL_datum *)row);
+							plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW,
+											   row->rowno,
+											   $1.name);
+
+						}
 					}
 				| decl_varname K_RECORD ';'
 					{
-						PLpgSQL_rec		*new;
+						PLpgSQL_rec		*var;
 
-						new = malloc(sizeof(PLpgSQL_rec));
+						var = malloc(sizeof(PLpgSQL_rec));
 
-						new->dtype		= PLPGSQL_DTYPE_REC;
-						new->refname	= $1.name;
-						new->lineno		= $1.lineno;
+						var->dtype		= PLPGSQL_DTYPE_REC;
+						var->refname	= $1.name;
+						var->lineno		= $1.lineno;
 
-						plpgsql_adddatum((PLpgSQL_datum *)new);
-						plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, new->recno,
+						plpgsql_adddatum((PLpgSQL_datum *)var);
+						plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, var->recno,
 												$1.name);
 					}
 				| decl_varname decl_rowtype ';'
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index feb80f94ae7..5c88761e05d 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.56 2003/04/24 21:16:44 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.57 2003/04/27 22:21:22 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -81,7 +81,7 @@ PLpgSQL_function *plpgsql_curr_compile;
 
 
 static void plpgsql_compile_error_callback(void *arg);
-static PLpgSQL_row *build_rowtype(Oid classOid);
+static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
 
 
 /*
@@ -275,19 +275,11 @@ plpgsql_compile(Oid fn_oid, int functype)
 					 */
 					var = malloc(sizeof(PLpgSQL_var));
 					memset(var, 0, sizeof(PLpgSQL_var));
-					var->datatype = malloc(sizeof(PLpgSQL_type));
-					memset(var->datatype, 0, sizeof(PLpgSQL_type));
 
 					var->dtype = PLPGSQL_DTYPE_VAR;
 					var->refname = strdup(buf);
 					var->lineno = 0;
-					var->datatype->typname = strdup(NameStr(typeStruct->typname));
-					var->datatype->typoid = procStruct->proargtypes[i];
-					perm_fmgr_info(typeStruct->typinput, &(var->datatype->typinput));
-					var->datatype->typelem = typeStruct->typelem;
-					var->datatype->typbyval = typeStruct->typbyval;
-					var->datatype->typlen = typeStruct->typlen;
-					var->datatype->atttypmod = -1;
+					var->datatype = build_datatype(typeTup, -1);
 					var->isconst = true;
 					var->notnull = false;
 					var->default_val = NULL;
@@ -908,7 +900,6 @@ plpgsql_parse_wordtype(char *word)
 		if (HeapTupleIsValid(typeTup))
 		{
 			Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
-			PLpgSQL_type *typ;
 
 			if (!typeStruct->typisdefined ||
 				typeStruct->typrelid != InvalidOid)
@@ -918,17 +909,7 @@ plpgsql_parse_wordtype(char *word)
 				return T_ERROR;
 			}
 
-			typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
-
-			typ->typname = strdup(NameStr(typeStruct->typname));
-			typ->typoid = typeOid;
-			perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
-			typ->typelem = typeStruct->typelem;
-			typ->typbyval = typeStruct->typbyval;
-			typ->typlen = typeStruct->typlen;
-			typ->atttypmod = -1;
-
-			plpgsql_yylval.dtype = typ;
+			plpgsql_yylval.dtype = build_datatype(typeTup, -1);
 
 			ReleaseSysCache(typeTup);
 			pfree(cp[0]);
@@ -960,8 +941,6 @@ plpgsql_parse_dblwordtype(char *word)
 	HeapTuple	attrtup;
 	Form_pg_attribute attrStruct;
 	HeapTuple	typetup;
-	Form_pg_type typeStruct;
-	PLpgSQL_type *typ;
 	char	   *cp[3];
 	int			i;
 
@@ -1067,22 +1046,11 @@ plpgsql_parse_dblwordtype(char *word)
 	if (!HeapTupleIsValid(typetup))
 		elog(ERROR, "cache lookup for type %u of %s.%s failed",
 			 attrStruct->atttypid, cp[0], cp[1]);
-	typeStruct = (Form_pg_type) GETSTRUCT(typetup);
 
 	/*
 	 * Found that - build a compiler type struct and return it
 	 */
-	typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
-
-	typ->typname = strdup(NameStr(typeStruct->typname));
-	typ->typoid = attrStruct->atttypid;
-	perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
-	typ->typelem = typeStruct->typelem;
-	typ->typbyval = typeStruct->typbyval;
-	typ->typlen = typeStruct->typlen;
-	typ->atttypmod = attrStruct->atttypmod;
-
-	plpgsql_yylval.dtype = typ;
+	plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod);
 
 	ReleaseSysCache(classtup);
 	ReleaseSysCache(attrtup);
@@ -1107,8 +1075,6 @@ plpgsql_parse_tripwordtype(char *word)
 	HeapTuple	attrtup;
 	Form_pg_attribute attrStruct;
 	HeapTuple	typetup;
-	Form_pg_type typeStruct;
-	PLpgSQL_type *typ;
 	char	   *cp[2];
 	char	   *colname[1];
 	int			qualified_att_len;
@@ -1192,22 +1158,11 @@ plpgsql_parse_tripwordtype(char *word)
 	if (!HeapTupleIsValid(typetup))
 		elog(ERROR, "cache lookup for type %u of %s.%s failed",
 			 attrStruct->atttypid, cp[0], cp[1]);
-	typeStruct = (Form_pg_type) GETSTRUCT(typetup);
 
 	/*
 	 * Found that - build a compiler type struct and return it
 	 */
-	typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
-
-	typ->typname = strdup(NameStr(typeStruct->typname));
-	typ->typoid = attrStruct->atttypid;
-	perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
-	typ->typelem = typeStruct->typelem;
-	typ->typbyval = typeStruct->typbyval;
-	typ->typlen = typeStruct->typlen;
-	typ->atttypmod = attrStruct->atttypmod;
-
-	plpgsql_yylval.dtype = typ;
+	plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod);
 
 	ReleaseSysCache(classtup);
 	ReleaseSysCache(attrtup);
@@ -1296,7 +1251,7 @@ plpgsql_parse_dblwordrowtype(char *word)
 /*
  * Build a rowtype data structure given the pg_class OID.
  */
-static PLpgSQL_row *
+PLpgSQL_row *
 build_rowtype(Oid classOid)
 {
 	PLpgSQL_row *row;
@@ -1341,7 +1296,6 @@ build_rowtype(Oid classOid)
 		HeapTuple	attrtup;
 		Form_pg_attribute attrStruct;
 		HeapTuple	typetup;
-		Form_pg_type typeStruct;
 		const char *attname;
 		PLpgSQL_var *var;
 
@@ -1365,7 +1319,6 @@ build_rowtype(Oid classOid)
 		if (!HeapTupleIsValid(typetup))
 			elog(ERROR, "cache lookup for type %u of %s.%s failed",
 				 attrStruct->atttypid, relname, attname);
-		typeStruct = (Form_pg_type) GETSTRUCT(typetup);
 
 		/*
 		 * Create the internal variable
@@ -1384,14 +1337,7 @@ build_rowtype(Oid classOid)
 		strcpy(var->refname, relname);
 		strcat(var->refname, ".");
 		strcat(var->refname, attname);
-		var->datatype = malloc(sizeof(PLpgSQL_type));
-		var->datatype->typname = strdup(NameStr(typeStruct->typname));
-		var->datatype->typoid = attrStruct->atttypid;
-		perm_fmgr_info(typeStruct->typinput, &(var->datatype->typinput));
-		var->datatype->typelem = typeStruct->typelem;
-		var->datatype->typbyval = typeStruct->typbyval;
-		var->datatype->typlen = typeStruct->typlen;
-		var->datatype->atttypmod = attrStruct->atttypmod;
+		var->datatype = build_datatype(typetup, attrStruct->atttypmod);
 		var->isconst = false;
 		var->notnull = false;
 		var->default_val = NULL;
@@ -1428,7 +1374,6 @@ plpgsql_parse_datatype(char *string)
 	Oid			type_id;
 	int32		typmod;
 	HeapTuple	typeTup;
-	Form_pg_type typeStruct;
 	PLpgSQL_type *typ;
 
 	/* Let the main parser try to parse it under standard SQL rules */
@@ -1440,20 +1385,34 @@ plpgsql_parse_datatype(char *string)
 							 0, 0, 0);
 	if (!HeapTupleIsValid(typeTup))
 		elog(ERROR, "cache lookup failed for type %u", type_id);
-	typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
+
+	typ = build_datatype(typeTup, typmod);
+
+	ReleaseSysCache(typeTup);
+
+	return typ;
+}
+
+/*
+ * Utility subroutine to make a PLpgSQL_type struct given a pg_type entry
+ */
+static PLpgSQL_type *
+build_datatype(HeapTuple typeTup, int32 typmod)
+{
+	Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
+	PLpgSQL_type *typ;
 
 	typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
 
 	typ->typname = strdup(NameStr(typeStruct->typname));
-	typ->typoid = type_id;
-	perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
-	typ->typelem = typeStruct->typelem;
-	typ->typbyval = typeStruct->typbyval;
+	typ->typoid = HeapTupleGetOid(typeTup);
 	typ->typlen = typeStruct->typlen;
+	typ->typbyval = typeStruct->typbyval;
+	typ->typrelid = typeStruct->typrelid;
+	typ->typelem = typeStruct->typelem;
+	perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
 	typ->atttypmod = typmod;
 
-	ReleaseSysCache(typeTup);
-
 	return typ;
 }
 
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 3756d94c911..1140cebddcc 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.34 2003/04/24 21:16:44 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.35 2003/04/27 22:21:22 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -142,14 +142,15 @@ typedef struct
 
 
 typedef struct
-{								/* Postgres base data type		*/
+{								/* Postgres data type		*/
 	char	   *typname;
-	Oid			typoid;
-	FmgrInfo	typinput;
-	Oid			typelem;
-	int16		typlen;
+	Oid			typoid;			/* OID of the data type */
+	int16		typlen;			/* stuff copied from its pg_type entry */
 	bool		typbyval;
-	int32		atttypmod;
+	Oid			typrelid;
+	Oid			typelem;
+	FmgrInfo	typinput;		/* lookup info for typinput function */
+	int32		atttypmod;		/* typmod (taken from someplace else) */
 }	PLpgSQL_type;
 
 
@@ -600,6 +601,7 @@ extern int	plpgsql_parse_tripwordtype(char *word);
 extern int	plpgsql_parse_wordrowtype(char *word);
 extern int	plpgsql_parse_dblwordrowtype(char *word);
 extern PLpgSQL_type *plpgsql_parse_datatype(char *string);
+extern PLpgSQL_row *build_rowtype(Oid classOid);
 extern void plpgsql_adddatum(PLpgSQL_datum * new);
 extern int	plpgsql_add_initdatums(int **varnos);
 extern void plpgsql_yyerror(const char *s);
-- 
GitLab