From 55ca02f447947416e66ef5355a0e4bf007c230c7 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 12 Aug 2002 14:25:07 +0000
Subject: [PATCH] Restructure rowtype-parameter handling to eliminate need for
 possibly- overflowable buffer for 'name%rowtype'; not to mention avoid
 problems with mixed-case type names and other special cases.

---
 src/pl/plpgsql/src/pl_comp.c | 93 +++++++++++++++++++++---------------
 1 file changed, 55 insertions(+), 38 deletions(-)

diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index d3c7cb0ef02..00edb1abf93 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.44 2002/08/08 01:36:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.45 2002/08/12 14:25:07 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -85,6 +85,9 @@ int			plpgsql_DumpExecTree = 0;
 PLpgSQL_function *plpgsql_curr_compile;
 
 
+static PLpgSQL_row *build_rowtype(Oid classOid);
+
+
 /*
  * This routine is a crock, and so is everyplace that calls it.  The problem
  * is that the compiled form of a plpgsql function is allocated permanently
@@ -234,7 +237,9 @@ plpgsql_compile(Oid fn_oid, int functype)
 			 */
 			for (i = 0; i < procStruct->pronargs; i++)
 			{
-				char		buf[256];
+				char		buf[32];
+
+				sprintf(buf, "$%d", i + 1);	/* name for variable */
 
 				/*
 				 * Get the parameters type
@@ -258,13 +263,7 @@ plpgsql_compile(Oid fn_oid, int functype)
 					 * For tuple type parameters, we set up a record of
 					 * that type
 					 */
-					sprintf(buf, "%s%%rowtype", NameStr(typeStruct->typname));
-					if (plpgsql_parse_wordrowtype(buf) != T_ROW)
-						elog(ERROR, "cannot get tuple struct of argument %d",
-							 i + 1);
-
-					row = plpgsql_yylval.row;
-					sprintf(buf, "$%d", i + 1);
+					row = build_rowtype(typeStruct->typrelid);
 
 					row->refname = strdup(buf);
 
@@ -284,7 +283,6 @@ plpgsql_compile(Oid fn_oid, int functype)
 					var->datatype = malloc(sizeof(PLpgSQL_type));
 					memset(var->datatype, 0, sizeof(PLpgSQL_type));
 
-					sprintf(buf, "$%d", i + 1);
 					var->dtype = PLPGSQL_DTYPE_VAR;
 					var->refname = strdup(buf);
 					var->lineno = 0;
@@ -1097,15 +1095,6 @@ int
 plpgsql_parse_wordrowtype(char *word)
 {
 	Oid			classOid;
-	HeapTuple	classtup;
-	Form_pg_class classStruct;
-	HeapTuple	typetup;
-	Form_pg_type typeStruct;
-	HeapTuple	attrtup;
-	Form_pg_attribute attrStruct;
-	PLpgSQL_row *row;
-	PLpgSQL_var *var;
-	char	   *attname;
 	char	   *cp[2];
 	int			i;
 
@@ -1116,25 +1105,51 @@ plpgsql_parse_wordrowtype(char *word)
 	word[i] = '.';
 	plpgsql_convert_ident(word, cp, 2);
 	word[i] = '%';
+
+	/* Lookup the relation */
+	classOid = RelnameGetRelid(cp[0]);
+	if (!OidIsValid(classOid))
+		elog(ERROR, "%s: no such class", cp[0]);
+
+	/*
+	 * Build and return the complete row definition
+	 */
+	plpgsql_yylval.row = build_rowtype(classOid);
+
+	pfree(cp[0]);
 	pfree(cp[1]);
 
+	return T_ROW;
+}
+
+/*
+ * Build a rowtype data structure given the pg_class OID.
+ */
+static PLpgSQL_row *
+build_rowtype(Oid classOid)
+{
+	PLpgSQL_row *row;
+	HeapTuple	classtup;
+	Form_pg_class classStruct;
+	const char  *relname;
+	int			i;
+
 	/*
 	 * Fetch the pg_class tuple.
 	 */
-	classOid = RelnameGetRelid(cp[0]);
-	if (!OidIsValid(classOid))
-		elog(ERROR, "%s: no such class", cp[0]);
 	classtup = SearchSysCache(RELOID,
 							  ObjectIdGetDatum(classOid),
 							  0, 0, 0);
 	if (!HeapTupleIsValid(classtup))
-		elog(ERROR, "%s: no such class", cp[0]);
+		elog(ERROR, "cache lookup failed for relation %u", classOid);
 	classStruct = (Form_pg_class) GETSTRUCT(classtup);
+	relname = NameStr(classStruct->relname);
+
 	/* accept relation, sequence, or view pg_class entries */
 	if (classStruct->relkind != RELKIND_RELATION &&
 		classStruct->relkind != RELKIND_SEQUENCE &&
 		classStruct->relkind != RELKIND_VIEW)
-		elog(ERROR, "%s isn't a table", cp[0]);
+		elog(ERROR, "%s isn't a table", relname);
 
 	/*
 	 * Create a row datum entry and all the required variables that it
@@ -1151,6 +1166,13 @@ plpgsql_parse_wordrowtype(char *word)
 
 	for (i = 0; i < row->nfields; i++)
 	{
+		HeapTuple	attrtup;
+		Form_pg_attribute attrStruct;
+		HeapTuple	typetup;
+		Form_pg_type typeStruct;
+		const char	   *attname;
+		PLpgSQL_var *var;
+
 		/*
 		 * Get the attribute and it's type
 		 */
@@ -1160,17 +1182,17 @@ plpgsql_parse_wordrowtype(char *word)
 								 0, 0);
 		if (!HeapTupleIsValid(attrtup))
 			elog(ERROR, "cache lookup for attribute %d of class %s failed",
-				 i + 1, cp[0]);
+				 i + 1, relname);
 		attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
 
-		attname = pstrdup(NameStr(attrStruct->attname));
+		attname = NameStr(attrStruct->attname);
 
 		typetup = SearchSysCache(TYPEOID,
 								 ObjectIdGetDatum(attrStruct->atttypid),
 								 0, 0, 0);
 		if (!HeapTupleIsValid(typetup))
 			elog(ERROR, "cache lookup for type %u of %s.%s failed",
-				 attrStruct->atttypid, cp[0], attname);
+				 attrStruct->atttypid, relname, attname);
 		typeStruct = (Form_pg_type) GETSTRUCT(typetup);
 
 		/*
@@ -1186,8 +1208,8 @@ plpgsql_parse_wordrowtype(char *word)
 		var = malloc(sizeof(PLpgSQL_var));
 		memset(var, 0, sizeof(PLpgSQL_var));
 		var->dtype = PLPGSQL_DTYPE_VAR;
-		var->refname = malloc(strlen(cp[0]) + strlen(attname) + 2);
-		strcpy(var->refname, cp[0]);
+		var->refname = malloc(strlen(relname) + strlen(attname) + 2);
+		strcpy(var->refname, relname);
 		strcat(var->refname, ".");
 		strcat(var->refname, attname);
 		var->datatype = malloc(sizeof(PLpgSQL_type));
@@ -1205,9 +1227,6 @@ plpgsql_parse_wordrowtype(char *word)
 		var->isnull = true;
 		var->freeval = false;
 
-		ReleaseSysCache(typetup);
-		ReleaseSysCache(attrtup);
-
 		plpgsql_adddatum((PLpgSQL_datum *) var);
 
 		/*
@@ -1215,16 +1234,14 @@ plpgsql_parse_wordrowtype(char *word)
 		 */
 		row->fieldnames[i] = strdup(attname);
 		row->varnos[i] = var->varno;
+
+		ReleaseSysCache(typetup);
+		ReleaseSysCache(attrtup);
 	}
 
 	ReleaseSysCache(classtup);
 
-	/*
-	 * Return the complete row definition
-	 */
-	plpgsql_yylval.row = row;
-
-	return T_ROW;
+	return row;
 }
 
 
-- 
GitLab