diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 1c8fa43b219d74816fa7ee2a9fdd2f9dcd443bb8..210d14427740fad735a7a3f110ece2aee08b884c 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.65 2005/02/13 01:25:50 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.66 2005/02/22 07:18:24 neilc Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -38,18 +38,24 @@
 
 #include "plpgsql.h"
 
+#include "parser/parser.h"
 
-static	PLpgSQL_expr	*read_sql_construct(int until,
+static PLpgSQL_expr		*read_sql_construct(int until,
 											int until2,
 											const char *expected,
-											bool isexpression,
 											const char *sqlstart,
+											bool isexpression,
+											bool valid_sql,
 											int *endtoken);
 static	PLpgSQL_expr	*read_sql_stmt(const char *sqlstart);
 static	PLpgSQL_type	*read_datatype(int tok);
 static	PLpgSQL_stmt	*make_select_stmt(void);
 static	PLpgSQL_stmt	*make_fetch_stmt(void);
-static	void check_assignable(PLpgSQL_datum *datum);
+static	void			 check_assignable(PLpgSQL_datum *datum);
+static	PLpgSQL_row		*read_into_scalar_list(const char *initial_name,
+											   PLpgSQL_datum *initial_datum);
+static	void			 check_sql_expr(const char *stmt);
+static	void			 plpgsql_sql_error_callback(void *arg);
 
 %}
 
@@ -62,23 +68,11 @@ static	void check_assignable(PLpgSQL_datum *datum);
 			int  lineno;
 		}						varname;
 		struct
-		{
-			int  nalloc;
-			int  nused;
-			int	 *nums;
-		}						intlist;
-		struct
-		{
-			int  nalloc;
-			int  nused;
-			PLpgSQL_diag_item *dtitems;
-		}						dtlist;
-		struct
-		{
+		{    
 			char *name;
 			int  lineno;
-			PLpgSQL_rec	*rec;
-			PLpgSQL_row	*row;
+			PLpgSQL_rec     *rec;
+			PLpgSQL_row     *row;
 		}						forvariable;
 		struct
 		{
@@ -86,6 +80,7 @@ static	void check_assignable(PLpgSQL_datum *datum);
 			int  n_initvars;
 			int  *initvarnos;
 		}						declhdr;
+		List					*list;
 		PLpgSQL_type			*dtype;
 		PLpgSQL_datum			*scalar;	/* a VAR, RECFIELD, or TRIGARG */
 		PLpgSQL_variable		*variable;	/* a VAR, REC, or ROW */
@@ -94,12 +89,11 @@ static	void check_assignable(PLpgSQL_datum *datum);
 		PLpgSQL_rec				*rec;
 		PLpgSQL_expr			*expr;
 		PLpgSQL_stmt			*stmt;
-		PLpgSQL_stmts			*stmts;
 		PLpgSQL_stmt_block		*program;
 		PLpgSQL_condition		*condition;
 		PLpgSQL_exception		*exception;
-		PLpgSQL_exceptions		*exceptions;
 		PLpgSQL_nsitem			*nsitem;
+		PLpgSQL_diag_item		*diagitem;
 }
 
 %type <declhdr> decl_sect
@@ -108,7 +102,8 @@ static	void check_assignable(PLpgSQL_datum *datum);
 %type <ival>	decl_const decl_notnull
 %type <expr>	decl_defval decl_cursor_query
 %type <dtype>	decl_datatype
-%type <row>		decl_cursor_args decl_cursor_arglist
+%type <row>		decl_cursor_args
+%type <list>	decl_cursor_arglist
 %type <nsitem>	decl_aliasitem
 %type <str>		decl_stmts decl_stmt
 
@@ -126,7 +121,7 @@ static	void check_assignable(PLpgSQL_datum *datum);
 %type <str>		opt_exitlabel
 %type <str>		execsql_start
 
-%type <stmts>	proc_sect proc_stmts stmt_else loop_body
+%type <list>	proc_sect proc_stmts stmt_else loop_body
 %type <stmt>	proc_stmt pl_block
 %type <stmt>	stmt_assign stmt_if stmt_loop stmt_while stmt_exit
 %type <stmt>	stmt_return stmt_return_next stmt_raise stmt_execsql
@@ -134,16 +129,17 @@ static	void check_assignable(PLpgSQL_datum *datum);
 %type <stmt>	stmt_dynexecute stmt_getdiag
 %type <stmt>	stmt_open stmt_fetch stmt_close stmt_null
 
-%type <exceptions>	exception_sect proc_exceptions
+%type <list>	exception_sect proc_exceptions
 %type <exception>	proc_exception
 %type <condition>	proc_conditions
 
-%type <intlist>	raise_params
+%type <list>	raise_params
 %type <ival>	raise_level raise_param
 %type <str>		raise_msg
 
-%type <dtlist>	getdiag_list
-%type <ival>	getdiag_item getdiag_target
+%type <list>	getdiag_list
+%type <diagitem> getdiag_list_item
+%type <ival>	getdiag_kind getdiag_target
 
 %type <ival>	lno
 
@@ -240,7 +236,7 @@ comp_options	: comp_options comp_option
 
 comp_option		: O_OPTION O_DUMP
 					{
-						plpgsql_DumpExecTree = 1;
+						plpgsql_DumpExecTree = true;
 					}
 				;
 
@@ -252,8 +248,7 @@ pl_block		: decl_sect K_BEGIN lno proc_sect exception_sect K_END
 					{
 						PLpgSQL_stmt_block *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_block));
-						memset(new, 0, sizeof(PLpgSQL_stmt_block));
+						new = palloc0(sizeof(PLpgSQL_stmt_block));
 
 						new->cmd_type	= PLPGSQL_STMT_BLOCK;
 						new->lineno		= $3;
@@ -379,8 +374,7 @@ decl_statement	: decl_varname decl_const decl_datatype decl_notnull decl_defval
 																		  -1),
 												   true);
 
-						curname_def = malloc(sizeof(PLpgSQL_expr));
-						memset(curname_def, 0, sizeof(PLpgSQL_expr));
+						curname_def = palloc0(sizeof(PLpgSQL_expr));
 
 						curname_def->dtype = PLPGSQL_DTYPE_EXPR;
 						strcpy(buf, "SELECT '");
@@ -393,7 +387,7 @@ decl_statement	: decl_varname decl_const decl_datatype decl_notnull decl_defval
 							*cp2++ = *cp1++;
 						}
 						strcpy(cp2, "'::refcursor");
-						curname_def->query = strdup(buf);
+						curname_def->query = pstrdup(buf);
 						new->default_val = curname_def;
 
 						new->cursor_explicit_expr = $6;
@@ -421,63 +415,41 @@ decl_cursor_args :
 						$$ = NULL;
 					}
 				| '(' decl_cursor_arglist ')'
-					{
-						/* Copy the temp arrays to malloc'd storage */
-						int nfields = $2->nfields;
-						char **ftmp;
-						int *vtmp;
-
-						ftmp = malloc(nfields * sizeof(char *));
-						vtmp = malloc(nfields * sizeof(int));
-						memcpy(ftmp, $2->fieldnames, nfields * sizeof(char *));
-						memcpy(vtmp, $2->varnos, nfields * sizeof(int));
-
-						pfree($2->fieldnames);
-						pfree($2->varnos);
-
-						$2->fieldnames = ftmp;
-						$2->varnos = vtmp;
-
-						plpgsql_adddatum((PLpgSQL_datum *)$2);
-
-						$$ = $2;
-					}
-				;
-
-decl_cursor_arglist : decl_cursor_arg
 					{
 						PLpgSQL_row *new;
+						int i;
+						ListCell *l;
 
-						new = malloc(sizeof(PLpgSQL_row));
-						memset(new, 0, sizeof(PLpgSQL_row));
-
+						new = palloc0(sizeof(PLpgSQL_row));
 						new->dtype = PLPGSQL_DTYPE_ROW;
-						new->refname = strdup("*internal*");
 						new->lineno = plpgsql_scanner_lineno();
 						new->rowtupdesc = NULL;
-						/*
-						 * We make temporary fieldnames/varnos arrays that
-						 * are much bigger than necessary.  We will resize
-						 * them to just the needed size in the
-						 * decl_cursor_args production.
-						 */
-						new->fieldnames = palloc(1024 * sizeof(char *));
-						new->varnos = palloc(1024 * sizeof(int));
-						new->nfields = 1;
+						new->nfields = list_length($2);
+						new->fieldnames = palloc(new->nfields * sizeof(char *));
+						new->varnos = palloc(new->nfields * sizeof(int));
 
-						new->fieldnames[0] = $1->refname;
-						new->varnos[0] = $1->dno;
+						i = 0;
+						foreach (l, $2)
+						{
+							PLpgSQL_variable *arg = (PLpgSQL_variable *) lfirst(l);
+							new->fieldnames[i] = arg->refname;
+							new->varnos[i] = arg->dno;
+							i++;
+						}
+						list_free($2);
 
+						plpgsql_adddatum((PLpgSQL_datum *) new);
 						$$ = new;
 					}
-				| decl_cursor_arglist ',' decl_cursor_arg
-					{
-						int i = $1->nfields++;
-
-						$1->fieldnames[i] = $3->refname;
-						$1->varnos[i] = $3->dno;
+				;
 
-						$$ = $1;
+decl_cursor_arglist : decl_cursor_arglist decl_cursor_arg
+					{
+						$$ = lappend($1, $2);
+					}
+				| decl_cursor_arg
+					{
+						$$ = list_make1($1);
 					}
 				;
 
@@ -524,10 +496,8 @@ decl_varname	: T_WORD
 						char	*name;
 
 						plpgsql_convert_ident(yytext, &name, 1);
-						/* name should be malloc'd for use as varname */
-						$$.name = strdup(name);
+						$$.name = name;
 						$$.lineno  = plpgsql_scanner_lineno();
-						pfree(name);
 					}
 				;
 
@@ -580,11 +550,7 @@ decl_defkey		: K_ASSIGN
 
 proc_sect		:
 					{
-							PLpgSQL_stmts	*new;
-
-							new = malloc(sizeof(PLpgSQL_stmts));
-							memset(new, 0, sizeof(PLpgSQL_stmts));
-							$$ = new;
+						$$ = NIL;
 					}
 				| proc_stmts
 					{ $$ = $1; }
@@ -592,31 +558,17 @@ proc_sect		:
 
 proc_stmts		: proc_stmts proc_stmt
 						{
-							if ($2 != NULL)
-							{
-								if ($1->stmts_used == $1->stmts_alloc)
-								{
-									$1->stmts_alloc *= 2;
-									$1->stmts = realloc($1->stmts, sizeof(PLpgSQL_stmt *) * $1->stmts_alloc);
-								}
-								$1->stmts[$1->stmts_used++] = $2;
-							}
-							$$ = $1;
+							if ($2 == NULL)
+								$$ = $1;
+							else
+								$$ = lappend($1, $2);
 						}
 				| proc_stmt
 						{
-							PLpgSQL_stmts	*new;
-
-							new = malloc(sizeof(PLpgSQL_stmts));
-							memset(new, 0, sizeof(PLpgSQL_stmts));
-
-							new->stmts_alloc = 32;
-							new->stmts = malloc(sizeof(PLpgSQL_stmt *) * new->stmts_alloc);
-
-							if ($1 != NULL)
-								new->stmts[new->stmts_used++] = $1;
-
-							$$ = new;
+							if ($1 == NULL)
+								$$ = NULL;
+							else
+								$$ = list_make1($1);
 						}
 				;
 
@@ -664,9 +616,7 @@ stmt_perform	: K_PERFORM lno expr_until_semi
 					{
 						PLpgSQL_stmt_perform *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_perform));
-						memset(new, 0, sizeof(PLpgSQL_stmt_perform));
-
+						new = palloc0(sizeof(PLpgSQL_stmt_perform));
 						new->cmd_type = PLPGSQL_STMT_PERFORM;
 						new->lineno   = $2;
 						new->expr  = $3;
@@ -679,9 +629,7 @@ stmt_assign		: assign_var lno K_ASSIGN expr_until_semi
 					{
 						PLpgSQL_stmt_assign *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_assign));
-						memset(new, 0, sizeof(PLpgSQL_stmt_assign));
-
+						new = palloc0(sizeof(PLpgSQL_stmt_assign));
 						new->cmd_type = PLPGSQL_STMT_ASSIGN;
 						new->lineno   = $2;
 						new->varno = $1;
@@ -695,45 +643,38 @@ stmt_getdiag	: K_GET K_DIAGNOSTICS lno getdiag_list ';'
 					{
 						PLpgSQL_stmt_getdiag	 *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_getdiag));
-						memset(new, 0, sizeof(PLpgSQL_stmt_getdiag));
-
+						new = palloc0(sizeof(PLpgSQL_stmt_getdiag));
 						new->cmd_type = PLPGSQL_STMT_GETDIAG;
 						new->lineno   = $3;
-						new->ndtitems = $4.nused;
-						new->dtitems  = malloc(sizeof(PLpgSQL_diag_item) * $4.nused);
-						memcpy(new->dtitems, $4.dtitems, sizeof(PLpgSQL_diag_item) * $4.nused);
+						new->diag_items  = $4;
 
 						$$ = (PLpgSQL_stmt *)new;
 					}
 				;
 
-getdiag_list : getdiag_list ',' getdiag_target K_ASSIGN getdiag_item
+getdiag_list : getdiag_list ',' getdiag_list_item
 					{
-						if ($1.nused == $1.nalloc)
-						{
-							$1.nalloc *= 2;
-							$1.dtitems = repalloc($1.dtitems, sizeof(PLpgSQL_diag_item) * $1.nalloc);
-						}
-						$1.dtitems[$1.nused].target = $3;
-						$1.dtitems[$1.nused].item   = $5;
-						$1.nused++;
-
-						$$.nalloc = $1.nalloc;
-						$$.nused  = $1.nused;
-						$$.dtitems = $1.dtitems;
+						$$ = lappend($1, $3);
 					}
-				| getdiag_target K_ASSIGN getdiag_item
+				| getdiag_list_item
 					{
-						$$.nalloc = 1;
-						$$.nused  = 1;
-						$$.dtitems = palloc(sizeof(PLpgSQL_diag_item) * $$.nalloc);
-						$$.dtitems[0].target = $1;
-						$$.dtitems[0].item   = $3;
+						$$ = list_make1($1);
 					}
 				;
 
-getdiag_item : K_ROW_COUNT
+getdiag_list_item : getdiag_target K_ASSIGN getdiag_kind
+					{
+						PLpgSQL_diag_item *new;
+
+						new = palloc(sizeof(PLpgSQL_diag_item));
+						new->target = $1;
+						new->kind = $3;
+
+						$$ = new;
+					}
+				;
+
+getdiag_kind : K_ROW_COUNT
 					{
 						$$ = PLPGSQL_GETDIAG_ROW_COUNT;
 					}
@@ -770,9 +711,7 @@ assign_var		: T_SCALAR
 					{
 						PLpgSQL_arrayelem	*new;
 
-						new = malloc(sizeof(PLpgSQL_arrayelem));
-						memset(new, 0, sizeof(PLpgSQL_arrayelem));
-
+						new = palloc0(sizeof(PLpgSQL_arrayelem));
 						new->dtype		= PLPGSQL_DTYPE_ARRAYELEM;
 						new->subscript	= $3;
 						new->arrayparentno = $1;
@@ -787,9 +726,7 @@ stmt_if			: K_IF lno expr_until_then proc_sect stmt_else K_END K_IF ';'
 					{
 						PLpgSQL_stmt_if *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_if));
-						memset(new, 0, sizeof(PLpgSQL_stmt_if));
-
+						new = palloc0(sizeof(PLpgSQL_stmt_if));
 						new->cmd_type	= PLPGSQL_STMT_IF;
 						new->lineno		= $2;
 						new->cond		= $3;
@@ -802,53 +739,35 @@ stmt_if			: K_IF lno expr_until_then proc_sect stmt_else K_END K_IF ';'
 
 stmt_else		:
 					{
-							PLpgSQL_stmts	*new;
-
-							new = malloc(sizeof(PLpgSQL_stmts));
-							memset(new, 0, sizeof(PLpgSQL_stmts));
-							$$ = new;
+						$$ = NIL;
 					}
 				| K_ELSIF lno expr_until_then proc_sect stmt_else
 					{
-					  /*
-					   * Translate the structure:	   into:
-					   *
-					   * IF c1 THEN					   IF c1 THEN		 
-					   *	 ...						   ...				 
-					   * ELSIF c2 THEN				   ELSE 
-					   *								   IF c2 THEN	
-					   *	 ...							   ...				 
-					   * ELSE							   ELSE				 
-					   *	 ...							   ...				 
-					   * END IF							   END IF			 
-					   *							   END IF
-					   * 
-					   */
-
-						PLpgSQL_stmts	*new;
+						/*
+						 * Translate the structure:	   into:
+						 *
+						 * IF c1 THEN				   IF c1 THEN
+						 *	 ...						   ...
+						 * ELSIF c2 THEN			   ELSE
+						 *								   IF c2 THEN
+						 *	 ...							   ...
+						 * ELSE							   ELSE
+						 *	 ...							   ...
+						 * END IF						   END IF			 
+						 *							   END IF
+						 */
 						PLpgSQL_stmt_if *new_if;
 
 						/* first create a new if-statement */
-						new_if = malloc(sizeof(PLpgSQL_stmt_if));
-						memset(new_if, 0, sizeof(PLpgSQL_stmt_if));
-
+						new_if = palloc0(sizeof(PLpgSQL_stmt_if));
 						new_if->cmd_type	= PLPGSQL_STMT_IF;
 						new_if->lineno		= $2;
 						new_if->cond		= $3;
 						new_if->true_body	= $4;
 						new_if->false_body	= $5;
-						
-						/* this is a 'container' for the if-statement */
-						new = malloc(sizeof(PLpgSQL_stmts));
-						memset(new, 0, sizeof(PLpgSQL_stmts));
-						
-						new->stmts_alloc = 64;
-						new->stmts_used	 = 1;
-						new->stmts = malloc(sizeof(PLpgSQL_stmt *) * new->stmts_alloc);
-						new->stmts[0] = (PLpgSQL_stmt *) new_if;
 
-						$$ = new;
-						
+						/* wrap the if-statement in a "container" list */
+						$$ = list_make1(new_if);
 					}
 
 				| K_ELSE proc_sect
@@ -861,9 +780,7 @@ stmt_loop		: opt_label K_LOOP lno loop_body
 					{
 						PLpgSQL_stmt_loop *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_loop));
-						memset(new, 0, sizeof(PLpgSQL_stmt_loop));
-
+						new = palloc0(sizeof(PLpgSQL_stmt_loop));
 						new->cmd_type = PLPGSQL_STMT_LOOP;
 						new->lineno   = $3;
 						new->label	  = $1;
@@ -879,9 +796,7 @@ stmt_while		: opt_label K_WHILE lno expr_until_loop loop_body
 					{
 						PLpgSQL_stmt_while *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_while));
-						memset(new, 0, sizeof(PLpgSQL_stmt_while));
-
+						new = palloc0(sizeof(PLpgSQL_stmt_while));
 						new->cmd_type = PLPGSQL_STMT_WHILE;
 						new->lineno   = $3;
 						new->label	  = $1;
@@ -931,86 +846,20 @@ stmt_for		: opt_label K_FOR for_control loop_body
 					}
 				;
 
-for_control		: lno for_variable K_IN
+for_control		:
+				lno for_variable K_IN
 					{
-						int			tok;
-						bool		reverse = false;
-						bool		execute = false;
-						PLpgSQL_expr *expr1;
-
-						/* check for REVERSE and EXECUTE */
-						tok = yylex();
-						if (tok == K_REVERSE)
-						{
-							reverse = true;
-							tok = yylex();
-						}
+						int			tok = yylex();
 
+						/* Simple case: EXECUTE is a dynamic FOR loop */
 						if (tok == K_EXECUTE)
-							execute = true;
-						else
-							plpgsql_push_back_token(tok);
-
-						/* Collect one or two expressions */
-						expr1 = read_sql_construct(K_DOTDOT,
-												   K_LOOP,
-												   "LOOP",
-												   true,
-												   "SELECT ",
-												   &tok);
-
-						if (tok == K_DOTDOT)
 						{
-							/* Found .., so it must be an integer loop */
-							PLpgSQL_stmt_fori	*new;
-							PLpgSQL_expr		*expr2;
-							PLpgSQL_var			*fvar;
-
-							expr2 = plpgsql_read_expression(K_LOOP, "LOOP");
-
-							if (execute)
-							{
-								plpgsql_error_lineno = $1;
-								yyerror("cannot specify EXECUTE in integer for-loop");
-							}
-
-							/* name should be malloc'd for use as varname */
-							fvar = (PLpgSQL_var *)
-								plpgsql_build_variable(strdup($2.name),
-													   $2.lineno,
-													   plpgsql_build_datatype(INT4OID,
-																			  -1),
-													   true);
-
-							/* put the for-variable into the local block */
-							plpgsql_add_initdatums(NULL);
-
-							new = malloc(sizeof(PLpgSQL_stmt_fori));
-							memset(new, 0, sizeof(PLpgSQL_stmt_fori));
-
-							new->cmd_type = PLPGSQL_STMT_FORI;
-							new->lineno   = $1;
-							new->var	  = fvar;
-							new->reverse  = reverse;
-							new->lower	  = expr1;
-							new->upper	  = expr2;
-
-							$$ = (PLpgSQL_stmt *) new;
-						}
-						else if (execute)
-						{
-							/* No .., so it must be a loop over rows */
 							PLpgSQL_stmt_dynfors	*new;
+							PLpgSQL_expr			*expr;
 
-							if (reverse)
-							{
-								plpgsql_error_lineno = $1;
-								yyerror("cannot specify REVERSE in loop over rows");
-							}
-
-							new = malloc(sizeof(PLpgSQL_stmt_dynfors));
-							memset(new, 0, sizeof(PLpgSQL_stmt_dynfors));
+							expr = plpgsql_read_expression(K_LOOP, "LOOP");
 
+							new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
 							new->cmd_type = PLPGSQL_STMT_DYNFORS;
 							new->lineno   = $1;
 							if ($2.rec)
@@ -1022,47 +871,115 @@ for_control		: lno for_variable K_IN
 								plpgsql_error_lineno = $1;
 								yyerror("loop variable of loop over rows must be a record or row variable");
 							}
-							new->query = expr1;
+							new->query = expr;
 
 							$$ = (PLpgSQL_stmt *) new;
 						}
 						else
 						{
-							/* No .., so it must be a loop over rows */
-							PLpgSQL_stmt_fors		*new;
-							char					*newquery;
+							PLpgSQL_expr	*expr1;
+							bool			 reverse = false;
+
+							/*
+							 * We have to distinguish between two
+							 * alternatives: FOR var IN a .. b and FOR
+							 * var IN query. Unfortunately this is
+							 * tricky, since the query in the second
+							 * form needn't start with a SELECT
+							 * keyword.  We use the ugly hack of
+							 * looking for two periods after the first
+							 * token. We also check for the REVERSE
+							 * keyword, which means it must be an
+							 * integer loop.
+							 */
+							if (tok == K_REVERSE)
+								reverse = true;
+							else
+								plpgsql_push_back_token(tok);
 
-							if (reverse)
+							/*
+							 * Read tokens until we see either a ".."
+							 * or a LOOP. The text we read may not
+							 * necessarily be a well-formed SQL
+							 * statement, so we need to invoke
+							 * read_sql_construct directly.
+							 */
+							expr1 = read_sql_construct(K_DOTDOT,
+													   K_LOOP,
+													   "LOOP",
+													   "SELECT ",
+													   true,
+													   false,
+													   &tok);
+
+							if (tok == K_DOTDOT)
 							{
-								plpgsql_error_lineno = $1;
-								yyerror("cannot specify REVERSE in loop over rows");
+								/* Saw "..", so it must be an integer loop */
+								PLpgSQL_expr		*expr2;
+								PLpgSQL_var			*fvar;
+								PLpgSQL_stmt_fori	*new;
+
+								/* First expression is well-formed */
+								check_sql_expr(expr1->query);
+
+								expr2 = plpgsql_read_expression(K_LOOP, "LOOP");
+
+								fvar = (PLpgSQL_var *)
+									plpgsql_build_variable($2.name,
+														   $2.lineno,
+														   plpgsql_build_datatype(INT4OID,
+																				  -1),
+														   true);
+
+								/* put the for-variable into the local block */
+								plpgsql_add_initdatums(NULL);
+
+								new = palloc0(sizeof(PLpgSQL_stmt_fori));
+								new->cmd_type = PLPGSQL_STMT_FORI;
+								new->lineno   = $1;
+								new->var	  = fvar;
+								new->reverse  = reverse;
+								new->lower	  = expr1;
+								new->upper	  = expr2;
+
+								$$ = (PLpgSQL_stmt *) new;
 							}
-
-							new = malloc(sizeof(PLpgSQL_stmt_fors));
-							memset(new, 0, sizeof(PLpgSQL_stmt_fors));
-
-							new->cmd_type = PLPGSQL_STMT_FORS;
-							new->lineno   = $1;
-							if ($2.rec)
-								new->rec = $2.rec;
-							else if ($2.row)
-								new->row = $2.row;
 							else
 							{
-								plpgsql_error_lineno = $1;
-								yyerror("loop variable of loop over rows must be a record or row variable");
-							}
-							/*
-							 * Must get rid of the "SELECT " we prepended
-							 * to expr1's text
-							 */
-							newquery = strdup(expr1->query + 7);
-							free(expr1->query);
-							expr1->query = newquery;
-
-							new->query = expr1;
+								/*
+								 * No "..", so it must be a query loop. We've prefixed an
+								 * extra SELECT to the query text, so we need to remove that
+								 * before performing syntax checking.
+								 */
+								char				*tmp_query;
+								PLpgSQL_stmt_fors	*new;
+
+								if (reverse)
+									yyerror("cannot specify REVERSE in query FOR loop");
+
+								Assert(strncmp(expr1->query, "SELECT ", 7) == 0);
+								tmp_query = pstrdup(expr1->query + 7);
+								pfree(expr1->query);
+								expr1->query = tmp_query;
+
+								check_sql_expr(expr1->query);
+
+								new = palloc0(sizeof(PLpgSQL_stmt_fors));
+								new->cmd_type = PLPGSQL_STMT_FORS;
+								new->lineno   = $1;
+								if ($2.rec)
+									new->rec = $2.rec;
+								else if ($2.row)
+									new->row = $2.row;
+								else
+								{
+									plpgsql_error_lineno = $1;
+									yyerror("loop variable of loop over rows must be record or row variable");
+								}
 
-							$$ = (PLpgSQL_stmt *) new;
+								new->query = expr1;
+								$$ = (PLpgSQL_stmt *) new;
+							}
 						}
 					}
 				;
@@ -1120,9 +1037,7 @@ stmt_exit		: K_EXIT lno opt_exitlabel opt_exitcond
 					{
 						PLpgSQL_stmt_exit *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_exit));
-						memset(new, 0, sizeof(PLpgSQL_stmt_exit));
-
+						new = palloc0(sizeof(PLpgSQL_stmt_exit));
 						new->cmd_type = PLPGSQL_STMT_EXIT;
 						new->lineno   = $2;
 						new->label	  = $3;
@@ -1136,8 +1051,7 @@ stmt_return		: K_RETURN lno
 					{
 						PLpgSQL_stmt_return *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_return));
-						memset(new, 0, sizeof(PLpgSQL_stmt_return));
+						new = palloc0(sizeof(PLpgSQL_stmt_return));
 						new->expr = NULL;
 						new->retrecno	= -1;
 						new->retrowno	= -1;
@@ -1169,9 +1083,18 @@ stmt_return		: K_RETURN lno
 							if (yylex() != ';')
 								yyerror("RETURN must specify a record or row variable in function returning tuple");
 						}
+						else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
+						{
+							if (yylex() != ';')
+								yyerror("function returning void cannot specify RETURN expression");
+						}
 						else
 						{
-							/* ordinary expression case */
+							/*
+							 * Note that a well-formed expression is
+							 * _required_ here; anything else is a
+							 * compile-time error.
+							 */
 							new->expr = plpgsql_read_expression(';', ";");
 						}
 
@@ -1189,9 +1112,7 @@ stmt_return_next: K_RETURN_NEXT lno
 						if (!plpgsql_curr_compile->fn_retset)
 							yyerror("cannot use RETURN NEXT in a non-SETOF function");
 
-						new = malloc(sizeof(PLpgSQL_stmt_return_next));
-						memset(new, 0, sizeof(PLpgSQL_stmt_return_next));
-
+						new = palloc0(sizeof(PLpgSQL_stmt_return_next));
 						new->cmd_type	= PLPGSQL_STMT_RETURN_NEXT;
 						new->lineno		= $2;
 
@@ -1220,15 +1141,13 @@ stmt_raise		: K_RAISE lno raise_level raise_msg raise_params ';'
 					{
 						PLpgSQL_stmt_raise		*new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_raise));
+						new = palloc(sizeof(PLpgSQL_stmt_raise));
 
 						new->cmd_type	= PLPGSQL_STMT_RAISE;
 						new->lineno		= $2;
 						new->elog_level = $3;
 						new->message	= $4;
-						new->nparams	= $5.nused;
-						new->params		= malloc(sizeof(int) * $5.nused);
-						memcpy(new->params, $5.nums, sizeof(int) * $5.nused);
+						new->params		= $5;
 
 						$$ = (PLpgSQL_stmt *)new;
 					}
@@ -1236,14 +1155,13 @@ stmt_raise		: K_RAISE lno raise_level raise_msg raise_params ';'
 					{
 						PLpgSQL_stmt_raise		*new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_raise));
+						new = palloc(sizeof(PLpgSQL_stmt_raise));
 
 						new->cmd_type	= PLPGSQL_STMT_RAISE;
 						new->lineno		= $2;
 						new->elog_level = $3;
 						new->message	= $4;
-						new->nparams	= 0;
-						new->params		= NULL;
+						new->params		= NIL;
 
 						$$ = (PLpgSQL_stmt *)new;
 					}
@@ -1283,23 +1201,11 @@ raise_level		: K_EXCEPTION
 
 raise_params	: raise_params raise_param
 					{
-						if ($1.nused == $1.nalloc)
-						{
-							$1.nalloc *= 2;
-							$1.nums = repalloc($1.nums, sizeof(int) * $1.nalloc);
-						}
-						$1.nums[$1.nused++] = $2;
-
-						$$.nalloc = $1.nalloc;
-						$$.nused  = $1.nused;
-						$$.nums   = $1.nums;
+						$$ = lappend_int($1, $2);
 					}
 				| raise_param
 					{
-						$$.nalloc = 1;
-						$$.nused  = 1;
-						$$.nums   = palloc(sizeof(int) * $$.nalloc);
-						$$.nums[0] = $1;
+						$$ = list_make1_int($1);
 					}
 				;
 
@@ -1317,7 +1223,7 @@ stmt_execsql	: execsql_start lno
 					{
 						PLpgSQL_stmt_execsql	*new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_execsql));
+						new = palloc(sizeof(PLpgSQL_stmt_execsql));
 						new->cmd_type = PLPGSQL_STMT_EXECSQL;
 						new->lineno   = $2;
 						new->sqlstmt  = read_sql_stmt($1);
@@ -1327,16 +1233,16 @@ stmt_execsql	: execsql_start lno
 				;
 
 stmt_dynexecute : K_EXECUTE lno expr_until_semi
-						{
-							PLpgSQL_stmt_dynexecute *new;
+					{
+						PLpgSQL_stmt_dynexecute *new;
 
-							new = malloc(sizeof(PLpgSQL_stmt_dynexecute));
-							new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
-							new->lineno   = $2;
-							new->query	  = $3;
+						new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
+						new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
+						new->lineno   = $2;
+						new->query	  = $3;
 
-							$$ = (PLpgSQL_stmt *)new;
-						}
+						$$ = (PLpgSQL_stmt *)new;
+					}
 				;
 
 stmt_open		: K_OPEN lno cursor_varptr
@@ -1344,9 +1250,7 @@ stmt_open		: K_OPEN lno cursor_varptr
 						PLpgSQL_stmt_open *new;
 						int				  tok;
 
-						new = malloc(sizeof(PLpgSQL_stmt_open));
-						memset(new, 0, sizeof(PLpgSQL_stmt_open));
-
+						new = palloc0(sizeof(PLpgSQL_stmt_open));
 						new->cmd_type = PLPGSQL_STMT_OPEN;
 						new->lineno = $2;
 						new->curvar = $3->varno;
@@ -1488,7 +1392,7 @@ stmt_close		: K_CLOSE lno cursor_variable ';'
 					{
 						PLpgSQL_stmt_close *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_close));
+						new = palloc(sizeof(PLpgSQL_stmt_close));
 						new->cmd_type = PLPGSQL_STMT_CLOSE;
 						new->lineno = $2;
 						new->curvar = $3;
@@ -1539,41 +1443,24 @@ cursor_variable	: T_SCALAR
 				;
 
 execsql_start	: T_WORD
-					{ $$ = strdup(yytext); }
+					{ $$ = pstrdup(yytext); }
 				| T_ERROR
-					{ $$ = strdup(yytext); }
+					{ $$ = pstrdup(yytext); }
 				;
 
 exception_sect	:
-					{ $$ = NULL; }
+					{ $$ = NIL; }
 				| K_EXCEPTION proc_exceptions
 					{ $$ = $2; }
 				;
 
 proc_exceptions	: proc_exceptions proc_exception
 						{
-								if ($1->exceptions_used == $1->exceptions_alloc)
-								{
-									$1->exceptions_alloc *= 2;
-									$1->exceptions = realloc($1->exceptions, sizeof(PLpgSQL_exception *) * $1->exceptions_alloc);
-								}
-								$1->exceptions[$1->exceptions_used++] = $2;
-
-								$$ = $1;
+							$$ = lappend($1, $2);
 						}
 				| proc_exception
 						{
-								PLpgSQL_exceptions	*new;
-
-								new = malloc(sizeof(PLpgSQL_exceptions));
-								memset(new, 0, sizeof(PLpgSQL_exceptions));
-
-								new->exceptions_alloc = 16;
-								new->exceptions_used  = 1;
-								new->exceptions = malloc(sizeof(PLpgSQL_exception *) * new->exceptions_alloc);
-								new->exceptions[0] = $1;
-
-								$$ = new;
+							$$ = list_make1($1);
 						}
 				;
 
@@ -1581,9 +1468,7 @@ proc_exception	: K_WHEN lno proc_conditions K_THEN proc_sect
 					{
 						PLpgSQL_exception *new;
 
-						new = malloc(sizeof(PLpgSQL_exception));
-						memset(new, 0, sizeof(PLpgSQL_exception));
-
+						new = palloc0(sizeof(PLpgSQL_exception));
 						new->lineno     = $2;
 						new->conditions = $3;
 						new->action	    = $5;
@@ -1643,8 +1528,7 @@ opt_exitlabel	:
 						char	*name;
 
 						plpgsql_convert_ident(yytext, &name, 1);
-						$$ = strdup(name);
-						pfree(name);
+						$$ = name;
 					}
 				| T_WORD
 					{
@@ -1664,8 +1548,7 @@ opt_lblname		: T_WORD
 						char	*name;
 
 						plpgsql_convert_ident(yytext, &name, 1);
-						$$ = strdup(name);
-						pfree(name);
+						$$ = name;
 					}
 				;
 
@@ -1681,13 +1564,13 @@ lno				:
 PLpgSQL_expr *
 plpgsql_read_expression(int until, const char *expected)
 {
-	return read_sql_construct(until, 0, expected, true, "SELECT ", NULL);
+	return read_sql_construct(until, 0, expected, "SELECT ", true, true, NULL);
 }
 
 static PLpgSQL_expr *
 read_sql_stmt(const char *sqlstart)
 {
-	return read_sql_construct(';', 0, ";", false, sqlstart, NULL);
+	return read_sql_construct(';', 0, ";", sqlstart, false, true, NULL);
 }
 
 /*
@@ -1696,8 +1579,9 @@ read_sql_stmt(const char *sqlstart)
  * until:		token code for expected terminator
  * until2:		token code for alternate terminator (pass 0 if none)
  * expected:	text to use in complaining that terminator was not found
- * isexpression: whether to say we're reading an "expression" or a "statement"
  * sqlstart:	text to prefix to the accumulated SQL text
+ * isexpression: whether to say we're reading an "expression" or a "statement"
+ * valid_sql:   whether to check the syntax of the expression (plus sqlstart)
  * endtoken:	if not NULL, ending token is stored at *endtoken
  *				(this is only interesting if until2 isn't zero)
  */
@@ -1705,8 +1589,9 @@ static PLpgSQL_expr *
 read_sql_construct(int until,
 				   int until2,
 				   const char *expected,
-				   bool isexpression,
 				   const char *sqlstart,
+				   bool isexpression,
+				   bool valid_sql,
 				   int *endtoken)
 {
 	int					tok;
@@ -1762,8 +1647,19 @@ read_sql_construct(int until,
 						 errmsg("missing \"%s\" at end of SQL statement",
 								expected)));
 		}
+
 		if (plpgsql_SpaceScanned)
 			plpgsql_dstring_append(&ds, " ");
+
+		/* Check for array overflow */
+		if (nparams >= 1024)
+		{
+			plpgsql_error_lineno = lno;
+			ereport(ERROR,
+					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+					 errmsg("too many variables specified in SQL statement")));
+		}
+
 		switch (tok)
 		{
 			case T_SCALAR:
@@ -1793,15 +1689,18 @@ read_sql_construct(int until,
 	if (endtoken)
 		*endtoken = tok;
 
-	expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
+	expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
 	expr->dtype			= PLPGSQL_DTYPE_EXPR;
-	expr->query			= strdup(plpgsql_dstring_get(&ds));
+	expr->query			= pstrdup(plpgsql_dstring_get(&ds));
 	expr->plan			= NULL;
 	expr->nparams		= nparams;
 	while(nparams-- > 0)
 		expr->params[nparams] = params[nparams];
 	plpgsql_dstring_free(&ds);
 
+	if (valid_sql)
+		check_sql_expr(expr->query);
+
 	return expr;
 }
 
@@ -1870,7 +1769,6 @@ read_datatype(int tok)
 	return result;
 }
 
-
 static PLpgSQL_stmt *
 make_select_stmt(void)
 {
@@ -1881,18 +1779,16 @@ make_select_stmt(void)
 	PLpgSQL_expr		*expr;
 	PLpgSQL_row			*row = NULL;
 	PLpgSQL_rec			*rec = NULL;
-	int					tok = 0;
-	int					have_nexttok = 0;
-	int					have_into = 0;
+	int					tok;
+	bool				have_into = false;
 
 	plpgsql_dstring_init(&ds);
 	plpgsql_dstring_append(&ds, "SELECT ");
 
-	while(1)
+	while (1)
 	{
-		if (!have_nexttok)
-			tok = yylex();
-		have_nexttok = 0;
+		tok = yylex();
+
 		if (tok == ';')
 			break;
 		if (tok == 0)
@@ -1916,69 +1812,23 @@ make_select_stmt(void)
 			{
 				case T_ROW:
 					row = yylval.row;
-					have_into = 1;
+					have_into = true;
 					break;
 
 				case T_RECORD:
 					rec = yylval.rec;
-					have_into = 1;
+					have_into = true;
 					break;
 
 				case T_SCALAR:
-				{
-					int				nfields = 1;
-					char			*fieldnames[1024];
-					int				varnos[1024];
-
-					check_assignable(yylval.scalar);
-					fieldnames[0] = strdup(yytext);
-					varnos[0]	  = yylval.scalar->dno;
-
-					while ((tok = yylex()) == ',')
-					{
-						tok = yylex();
-						switch(tok)
-						{
-							case T_SCALAR:
-								check_assignable(yylval.scalar);
-								fieldnames[nfields] = strdup(yytext);
-								varnos[nfields++]	= yylval.scalar->dno;
-								break;
-
-							default:
-								plpgsql_error_lineno = plpgsql_scanner_lineno();
-								ereport(ERROR,
-										(errcode(ERRCODE_SYNTAX_ERROR),
-										 errmsg("\"%s\" is not a variable",
-												yytext)));
-						}
-					}
-					have_nexttok = 1;
-
-					row = malloc(sizeof(PLpgSQL_row));
-					row->dtype = PLPGSQL_DTYPE_ROW;
-					row->refname = strdup("*internal*");
-					row->lineno = plpgsql_scanner_lineno();
-					row->rowtupdesc = NULL;
-					row->nfields = nfields;
-					row->fieldnames = malloc(sizeof(char *) * nfields);
-					row->varnos = malloc(sizeof(int) * nfields);
-					while (--nfields >= 0)
-					{
-						row->fieldnames[nfields] = fieldnames[nfields];
-						row->varnos[nfields] = varnos[nfields];
-					}
-
-					plpgsql_adddatum((PLpgSQL_datum *)row);
-
-					have_into = 1;
-				}
-				break;
+					row = read_into_scalar_list(yytext, yylval.scalar);
+					have_into = true;
+					break;
 
 				default:
 					/* Treat the INTO as non-special */
 					plpgsql_dstring_append(&ds, " INTO ");
-					have_nexttok = 1;
+					plpgsql_push_back_token(tok);
 					break;
 			}
 			continue;
@@ -1986,6 +1836,16 @@ make_select_stmt(void)
 
 		if (plpgsql_SpaceScanned)
 			plpgsql_dstring_append(&ds, " ");
+
+		/* Check for array overflow */
+		if (nparams >= 1024)
+		{
+			plpgsql_error_lineno = plpgsql_scanner_lineno();
+			ereport(ERROR,
+					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+					 errmsg("too many parameters specified in SQL statement")));
+		}
+
 		switch (tok)
 		{
 			case T_SCALAR:
@@ -2012,9 +1872,9 @@ make_select_stmt(void)
 		}
 	}
 
-	expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
+	expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
 	expr->dtype			= PLPGSQL_DTYPE_EXPR;
-	expr->query			= strdup(plpgsql_dstring_get(&ds));
+	expr->query			= pstrdup(plpgsql_dstring_get(&ds));
 	expr->plan			= NULL;
 	expr->nparams		= nparams;
 	while(nparams-- > 0)
@@ -2025,8 +1885,7 @@ make_select_stmt(void)
 	{
 		PLpgSQL_stmt_select *select;
 
-		select = malloc(sizeof(PLpgSQL_stmt_select));
-		memset(select, 0, sizeof(PLpgSQL_stmt_select));
+		select = palloc0(sizeof(PLpgSQL_stmt_select));
 		select->cmd_type = PLPGSQL_STMT_SELECT;
 		select->rec		 = rec;
 		select->row		 = row;
@@ -2038,7 +1897,7 @@ make_select_stmt(void)
 	{
 		PLpgSQL_stmt_execsql *execsql;
 
-		execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
+		execsql = palloc(sizeof(PLpgSQL_stmt_execsql));
 		execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
 		execsql->sqlstmt  = expr;
 
@@ -2054,7 +1913,6 @@ make_fetch_stmt(void)
 	PLpgSQL_row		   *row = NULL;
 	PLpgSQL_rec		   *rec = NULL;
 	PLpgSQL_stmt_fetch *fetch;
-	int					have_nexttok = 0;
 
 	/* We have already parsed everything through the INTO keyword */
 
@@ -2070,66 +1928,18 @@ make_fetch_stmt(void)
 			break;
 
 		case T_SCALAR:
-			{
-				int				nfields = 1;
-				char			*fieldnames[1024];
-				int				varnos[1024];
-
-				check_assignable(yylval.scalar);
-				fieldnames[0] = strdup(yytext);
-				varnos[0]	  = yylval.scalar->dno;
-
-				while ((tok = yylex()) == ',')
-				{
-					tok = yylex();
-					switch(tok)
-					{
-						case T_SCALAR:
-							check_assignable(yylval.scalar);
-							fieldnames[nfields] = strdup(yytext);
-							varnos[nfields++]	= yylval.scalar->dno;
-							break;
-
-						default:
-							plpgsql_error_lineno = plpgsql_scanner_lineno();
-							ereport(ERROR,
-									(errcode(ERRCODE_SYNTAX_ERROR),
-									 errmsg("\"%s\" is not a variable",
-											yytext)));
-					}
-				}
-				have_nexttok = 1;
-
-				row = malloc(sizeof(PLpgSQL_row));
-				row->dtype = PLPGSQL_DTYPE_ROW;
-				row->refname = strdup("*internal*");
-				row->lineno = plpgsql_scanner_lineno();
-				row->rowtupdesc = NULL;
-				row->nfields = nfields;
-				row->fieldnames = malloc(sizeof(char *) * nfields);
-				row->varnos = malloc(sizeof(int) * nfields);
-				while (--nfields >= 0)
-				{
-					row->fieldnames[nfields] = fieldnames[nfields];
-					row->varnos[nfields] = varnos[nfields];
-				}
-
-				plpgsql_adddatum((PLpgSQL_datum *)row);
-			}
+			row = read_into_scalar_list(yytext, yylval.scalar);
 			break;
 
 		default:
 			yyerror("syntax error");
 	}
 
-	if (!have_nexttok)
-		tok = yylex();
-
+	tok = yylex();
 	if (tok != ';')
 		yyerror("syntax error");
 
-	fetch = malloc(sizeof(PLpgSQL_stmt_select));
-	memset(fetch, 0, sizeof(PLpgSQL_stmt_fetch));
+	fetch = palloc0(sizeof(PLpgSQL_stmt_select));
 	fetch->cmd_type = PLPGSQL_STMT_FETCH;
 	fetch->rec		 = rec;
 	fetch->row		 = row;
@@ -2174,4 +1984,143 @@ check_assignable(PLpgSQL_datum *datum)
 	}
 }
 
+/*
+ * Given the first datum and name in the INTO list, continue to read
+ * comma-separated scalar variables until we run out. Then construct
+ * and return a fake "row" variable that represents the list of
+ * scalars.
+ */
+static PLpgSQL_row *
+read_into_scalar_list(const char *initial_name,
+					  PLpgSQL_datum *initial_datum)
+{
+	int				 nfields;
+	char			*fieldnames[1024];
+	int				 varnos[1024];
+	PLpgSQL_row		*row;
+	int				 tok;
+
+	check_assignable(initial_datum);
+	fieldnames[0] = pstrdup(initial_name);
+	varnos[0]	  = initial_datum->dno;
+	nfields		  = 1;
+
+	while ((tok = yylex()) == ',')
+	{
+		/* Check for array overflow */
+		if (nfields >= 1024)
+		{
+			plpgsql_error_lineno = plpgsql_scanner_lineno();
+			ereport(ERROR,
+					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+					 errmsg("too many INTO variables specified")));
+		}
+
+		tok = yylex();
+		switch(tok)
+		{
+			case T_SCALAR:
+				check_assignable(yylval.scalar);
+				fieldnames[nfields] = pstrdup(yytext);
+				varnos[nfields++]	= yylval.scalar->dno;
+				break;
+
+			default:
+				plpgsql_error_lineno = plpgsql_scanner_lineno();
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("\"%s\" is not a variable",
+								yytext)));
+		}
+	}
+
+	/*
+	 * We read an extra, non-comma character from yylex(), so push it
+	 * back onto the input stream
+	 */
+	plpgsql_push_back_token(tok);
+
+	row = palloc(sizeof(PLpgSQL_row));
+	row->dtype = PLPGSQL_DTYPE_ROW;
+	row->refname = pstrdup("*internal*");
+	row->lineno = plpgsql_scanner_lineno();
+	row->rowtupdesc = NULL;
+	row->nfields = nfields;
+	row->fieldnames = palloc(sizeof(char *) * nfields);
+	row->varnos = palloc(sizeof(int) * nfields);
+	while (--nfields >= 0)
+	{
+		row->fieldnames[nfields] = fieldnames[nfields];
+		row->varnos[nfields] = varnos[nfields];
+	}
+
+	plpgsql_adddatum((PLpgSQL_datum *)row);
+
+	return row;
+}
+
+/*
+ * When the PL/PgSQL parser expects to see a SQL statement, it is very
+ * liberal in what it accepts; for example, we often assume an
+ * unrecognized keyword is the beginning of a SQL statement. This
+ * avoids the need to duplicate parts of the SQL grammar in the
+ * PL/PgSQL grammar, but it means we can accept wildly malformed
+ * input. To try and catch some of the more obviously invalid input,
+ * we run the strings we expect to be SQL statements through the main
+ * SQL parser.
+ *
+ * We only invoke the raw parser (not the analyzer); this doesn't do
+ * any database access and does not check any semantic rules, it just
+ * checks for basic syntactic correctness. We do this here, rather
+ * than after parsing has finished, because a malformed SQL statement
+ * may cause the PL/PgSQL parser to become confused about statement
+ * borders. So it is best to bail out as early as we can.
+ */
+static void
+check_sql_expr(const char *stmt)
+{
+	ErrorContextCallback  syntax_errcontext;
+	ErrorContextCallback *previous_errcontext;
+	MemoryContext oldCxt;
+
+	if (!plpgsql_check_syntax)
+		return;
+
+	/*
+	 * Setup error traceback support for ereport(). The previous
+	 * ereport callback is installed by pl_comp.c, but we don't want
+	 * that to be invoked (since it will try to transpose the syntax
+	 * error to be relative to the CREATE FUNCTION), so temporarily
+	 * remove it from the list of callbacks.
+	 */
+	Assert(error_context_stack->callback == plpgsql_compile_error_callback);
+
+	previous_errcontext = error_context_stack;
+	syntax_errcontext.callback = plpgsql_sql_error_callback;
+	syntax_errcontext.arg = (char *) stmt;
+	syntax_errcontext.previous = error_context_stack->previous;
+	error_context_stack = &syntax_errcontext;
+
+	oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
+	(void) raw_parser(stmt);
+	MemoryContextSwitchTo(oldCxt);
+
+	/* Restore former ereport callback */
+	error_context_stack = previous_errcontext;
+}
+
+static void
+plpgsql_sql_error_callback(void *arg)
+{
+	char *sql_stmt = (char *) arg;
+
+	Assert(plpgsql_error_funcname);
+
+	errcontext("SQL statement in PL/PgSQL function \"%s\" near line %d",
+			   plpgsql_error_funcname, plpgsql_error_lineno);
+	internalerrquery(sql_stmt);
+	internalerrposition(geterrposition());
+	errposition(0);
+}
+
 #include "pl_scan.c"
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index ff8ec94c8515cd6faba6a7bb588e34bcee1274d9..6ae93b7f204920b90a1170e8fa15350a9acd121e 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.83 2004/11/30 03:50:29 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.84 2005/02/22 07:18:24 neilc Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -76,10 +76,14 @@ static int	datums_last = 0;
 
 int			plpgsql_error_lineno;
 char	   *plpgsql_error_funcname;
-int			plpgsql_DumpExecTree = 0;
+bool		plpgsql_DumpExecTree = false;
+bool		plpgsql_check_syntax = false;
 
 PLpgSQL_function *plpgsql_curr_compile;
 
+/* A context appropriate for short-term allocs during compilation */
+MemoryContext compile_tmp_cxt;
+
 /* ----------
  * Hash table for compiled functions
  * ----------
@@ -118,7 +122,6 @@ static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
 		   HeapTuple procTup,
 		   PLpgSQL_func_hashkey *hashkey,
 		   bool forValidator);
-static void plpgsql_compile_error_callback(void *arg);
 static char **fetchArgNames(HeapTuple procTup, int nargs);
 static PLpgSQL_row *build_row_var(Oid classOid);
 static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
@@ -130,24 +133,7 @@ static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key)
 static void plpgsql_HashTableInsert(PLpgSQL_function *function,
 						PLpgSQL_func_hashkey *func_key);
 static void plpgsql_HashTableDelete(PLpgSQL_function *function);
-
-/*
- * 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
- * (mostly via malloc()) and never released until backend exit.  Subsidiary
- * data structures such as fmgr info records therefore must live forever
- * as well.  A better implementation would store all this stuff in a per-
- * function memory context that could be reclaimed at need.  In the meantime,
- * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever
- * it might allocate, and whatever the eventual function might allocate using
- * fn_mcxt, will live forever too.
- */
-static void
-perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
-{
-	fmgr_info_cxt(functionId, finfo, TopMemoryContext);
-}
-
+static void delete_function(PLpgSQL_function *func);
 
 /* ----------
  * plpgsql_compile		Make an execution tree for a PL/pgSQL function.
@@ -187,10 +173,6 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
 
 	if (!function)
 	{
-		/* First time through in this backend?	If so, init hashtable */
-		if (!plpgsql_HashTable)
-			plpgsql_HashTableInit();
-
 		/* Compute hashkey using function signature and actual arg types */
 		compute_function_hashkey(fcinfo, procStruct, &hashkey, forValidator);
 		hashkey_valid = true;
@@ -205,12 +187,8 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
 		if (!(function->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
 		   function->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data)))
 		{
-			/*
-			 * Nope, drop the hashtable entry.	XXX someday, free all the
-			 * subsidiary storage as well.
-			 */
-			plpgsql_HashTableDelete(function);
-
+			/* Nope, drop the function and associated storage */
+			delete_function(function);
 			function = NULL;
 		}
 	}
@@ -250,6 +228,19 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
 
 /*
  * This is the slow part of plpgsql_compile().
+ *
+ * While compiling a function, the CurrentMemoryContext is the
+ * per-function memory context of the function we are compiling. That
+ * means a palloc() will allocate storage with the same lifetime as
+ * the function itself.
+ *
+ * Because palloc()'d storage will not be immediately freed, temporary
+ * allocations should either be performed in a short-lived memory
+ * context or explicitly pfree'd. Since not all backend functions are
+ * careful about pfree'ing their allocations, it is also wise to
+ * switch into a short-term context before calling into the
+ * backend. An appropriate context for performing short-term
+ * allocations is the compile_tmp_cxt.
  */
 static PLpgSQL_function *
 do_compile(FunctionCallInfo fcinfo,
@@ -273,6 +264,7 @@ do_compile(FunctionCallInfo fcinfo,
 	int			parse_rc;
 	Oid			rettypeid;
 	char	  **argnames;
+	MemoryContext func_cxt;
 
 	/*
 	 * Setup the scanner input and error info.	We assume that this
@@ -293,7 +285,7 @@ do_compile(FunctionCallInfo fcinfo,
 	 * Setup error traceback support for ereport()
 	 */
 	plerrcontext.callback = plpgsql_compile_error_callback;
-	plerrcontext.arg = forValidator ? proc_source : (char *) NULL;
+	plerrcontext.arg = forValidator ? proc_source : NULL;
 	plerrcontext.previous = error_context_stack;
 	error_context_stack = &plerrcontext;
 
@@ -302,30 +294,46 @@ do_compile(FunctionCallInfo fcinfo,
 	 */
 	plpgsql_ns_init();
 	plpgsql_ns_push(NULL);
-	plpgsql_DumpExecTree = 0;
+	plpgsql_DumpExecTree = false;
 
 	datums_alloc = 128;
 	plpgsql_nDatums = 0;
+	/* This is short-lived, so needn't allocate in function's cxt */
 	plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);
 	datums_last = 0;
 
 	/*
-	 * Create the new function node
+	 * Do extra syntax checks when validating the function
+	 * definition. We skip this when actually compiling functions for
+	 * execution, for performance reasons.
 	 */
-	function = malloc(sizeof(PLpgSQL_function));
-	MemSet(function, 0, sizeof(PLpgSQL_function));
+	plpgsql_check_syntax = forValidator;
+
+	/*
+	 * Create the new function node. We allocate the function and all
+	 * of its compile-time storage (e.g. parse tree) in its own memory
+	 * context. This allows us to reclaim the function's storage
+	 * cleanly.
+	 */
+	func_cxt = AllocSetContextCreate(TopMemoryContext,
+									 "PL/PgSQL function context",
+									 ALLOCSET_DEFAULT_MINSIZE,
+									 ALLOCSET_DEFAULT_INITSIZE,
+									 ALLOCSET_DEFAULT_MAXSIZE);
+	compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
+	function = palloc0(sizeof(*function));
 	plpgsql_curr_compile = function;
 
-	function->fn_name = strdup(NameStr(procStruct->proname));
+	function->fn_name = pstrdup(NameStr(procStruct->proname));
 	function->fn_oid = fcinfo->flinfo->fn_oid;
 	function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
 	function->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
 	function->fn_functype = functype;
+	function->fn_cxt = func_cxt;
 
 	switch (functype)
 	{
 		case T_FUNCTION:
-
 			/*
 			 * Check for a polymorphic returntype. If found, use the
 			 * actual returntype type from the caller's FuncExpr node, if
@@ -398,7 +406,7 @@ do_compile(FunctionCallInfo fcinfo,
 				function->fn_retbyval = typeStruct->typbyval;
 				function->fn_rettyplen = typeStruct->typlen;
 				function->fn_rettypioparam = getTypeIOParam(typeTup);
-				perm_fmgr_info(typeStruct->typinput, &(function->fn_retinput));
+				fmgr_info(typeStruct->typinput, &(function->fn_retinput));
 
 				/*
 				 * install $0 reference, but only for polymorphic return
@@ -407,7 +415,7 @@ do_compile(FunctionCallInfo fcinfo,
 				if (procStruct->prorettype == ANYARRAYOID ||
 					procStruct->prorettype == ANYELEMENTOID)
 				{
-					(void) plpgsql_build_variable(strdup("$0"), 0,
+					(void) plpgsql_build_variable("$0", 0,
 											 build_datatype(typeTup, -1),
 												  true);
 				}
@@ -415,9 +423,13 @@ do_compile(FunctionCallInfo fcinfo,
 			ReleaseSysCache(typeTup);
 
 			/*
-			 * Create the variables for the procedure's parameters
+			 * Create the variables for the procedure's
+			 * parameters. Allocations aren't needed permanently, so
+			 * make them in tmp cxt.
 			 */
+			MemoryContextSwitchTo(compile_tmp_cxt);
 			argnames = fetchArgNames(procTup, procStruct->pronargs);
+			MemoryContextSwitchTo(func_cxt);
 
 			for (i = 0; i < procStruct->pronargs; i++)
 			{
@@ -449,7 +461,7 @@ do_compile(FunctionCallInfo fcinfo,
 								 format_type_be(argtypeid))));
 
 				/* Build variable and add to datum list */
-				argvariable = plpgsql_build_variable(strdup(buf), 0,
+				argvariable = plpgsql_build_variable(buf, 0,
 													 argdtype, false);
 
 				if (argvariable->dtype == PLPGSQL_DTYPE_VAR)
@@ -471,29 +483,23 @@ do_compile(FunctionCallInfo fcinfo,
 				plpgsql_ns_additem(argitemtype, argvariable->dno, buf);
 
 				/* If there's a name for the argument, make an alias */
-				if (argnames && argnames[i] && argnames[i][0])
+				if (argnames)
 					plpgsql_ns_additem(argitemtype, argvariable->dno,
 									   argnames[i]);
 			}
 			break;
 
 		case T_TRIGGER:
-
-			/*
-			 * Trigger procedures return type is unknown yet
-			 */
+			/* Trigger procedure's return type is unknown yet */
 			function->fn_rettype = InvalidOid;
 			function->fn_retbyval = false;
 			function->fn_retistuple = true;
 			function->fn_retset = false;
 
-			/*
-			 * Add the record for referencing NEW
-			 */
-			rec = malloc(sizeof(PLpgSQL_rec));
-			memset(rec, 0, sizeof(PLpgSQL_rec));
+			/* Add the record for referencing NEW */
+			rec = palloc0(sizeof(PLpgSQL_rec));
 			rec->dtype = PLPGSQL_DTYPE_REC;
-			rec->refname = strdup("new");
+			rec->refname = pstrdup("new");
 			rec->tup = NULL;
 			rec->tupdesc = NULL;
 			rec->freetup = false;
@@ -501,13 +507,10 @@ do_compile(FunctionCallInfo fcinfo,
 			plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname);
 			function->new_varno = rec->recno;
 
-			/*
-			 * Add the record for referencing OLD
-			 */
-			rec = malloc(sizeof(PLpgSQL_rec));
-			memset(rec, 0, sizeof(PLpgSQL_rec));
+			/* Add the record for referencing OLD */
+			rec = palloc0(sizeof(PLpgSQL_rec));
 			rec->dtype = PLPGSQL_DTYPE_REC;
-			rec->refname = strdup("old");
+			rec->refname = pstrdup("old");
 			rec->tup = NULL;
 			rec->tupdesc = NULL;
 			rec->freetup = false;
@@ -515,58 +518,44 @@ do_compile(FunctionCallInfo fcinfo,
 			plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname);
 			function->old_varno = rec->recno;
 
-			/*
-			 * Add the variable tg_name
-			 */
-			var = plpgsql_build_variable(strdup("tg_name"), 0,
+			/* Add the variable tg_name */
+			var = plpgsql_build_variable("tg_name", 0,
 									 plpgsql_build_datatype(NAMEOID, -1),
 										 true);
 			function->tg_name_varno = var->dno;
 
-			/*
-			 * Add the variable tg_when
-			 */
-			var = plpgsql_build_variable(strdup("tg_when"), 0,
+			/* Add the variable tg_when */
+			var = plpgsql_build_variable("tg_when", 0,
 									 plpgsql_build_datatype(TEXTOID, -1),
 										 true);
 			function->tg_when_varno = var->dno;
 
-			/*
-			 * Add the variable tg_level
-			 */
-			var = plpgsql_build_variable(strdup("tg_level"), 0,
+			/* Add the variable tg_level */
+			var = plpgsql_build_variable("tg_level", 0,
 									 plpgsql_build_datatype(TEXTOID, -1),
 										 true);
 			function->tg_level_varno = var->dno;
 
-			/*
-			 * Add the variable tg_op
-			 */
-			var = plpgsql_build_variable(strdup("tg_op"), 0,
+			/* Add the variable tg_op */
+			var = plpgsql_build_variable("tg_op", 0,
 									 plpgsql_build_datatype(TEXTOID, -1),
 										 true);
 			function->tg_op_varno = var->dno;
 
-			/*
-			 * Add the variable tg_relid
-			 */
-			var = plpgsql_build_variable(strdup("tg_relid"), 0,
+			/* Add the variable tg_relid */
+			var = plpgsql_build_variable("tg_relid", 0,
 									  plpgsql_build_datatype(OIDOID, -1),
 										 true);
 			function->tg_relid_varno = var->dno;
 
-			/*
-			 * Add the variable tg_relname
-			 */
-			var = plpgsql_build_variable(strdup("tg_relname"), 0,
+			/* Add the variable tg_relname */
+			var = plpgsql_build_variable("tg_relname", 0,
 									 plpgsql_build_datatype(NAMEOID, -1),
 										 true);
 			function->tg_relname_varno = var->dno;
 
-			/*
-			 * Add the variable tg_nargs
-			 */
-			var = plpgsql_build_variable(strdup("tg_nargs"), 0,
+			/* Add the variable tg_nargs */
+			var = plpgsql_build_variable("tg_nargs", 0,
 									 plpgsql_build_datatype(INT4OID, -1),
 										 true);
 			function->tg_nargs_varno = var->dno;
@@ -584,7 +573,7 @@ do_compile(FunctionCallInfo fcinfo,
 	/*
 	 * Create the magic FOUND variable.
 	 */
-	var = plpgsql_build_variable(strdup("found"), 0,
+	var = plpgsql_build_variable("found", 0,
 								 plpgsql_build_datatype(BOOLOID, -1),
 								 true);
 	function->found_varno = var->dno;
@@ -611,7 +600,7 @@ do_compile(FunctionCallInfo fcinfo,
 	for (i = 0; i < function->fn_nargs; i++)
 		function->fn_argvarnos[i] = arg_varnos[i];
 	function->ndatums = plpgsql_nDatums;
-	function->datums = malloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
+	function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
 	for (i = 0; i < plpgsql_nDatums; i++)
 		function->datums[i] = plpgsql_Datums[i];
 	function->action = plpgsql_yylval.program;
@@ -632,16 +621,21 @@ do_compile(FunctionCallInfo fcinfo,
 	plpgsql_error_funcname = NULL;
 	plpgsql_error_lineno = 0;
 
+	plpgsql_check_syntax = false;
+
+	MemoryContextSwitchTo(compile_tmp_cxt);
+	compile_tmp_cxt = NULL;
 	return function;
 }
 
 
 /*
- * error context callback to let us supply a call-stack traceback
- *
- * If we are validating, the function source is passed as argument.
+ * error context callback to let us supply a call-stack traceback. If
+ * we are validating, the function source is passed as an
+ * argument. This function is public only for the sake of an assertion
+ * in gram.y
  */
-static void
+void
 plpgsql_compile_error_callback(void *arg)
 {
 	if (arg)
@@ -725,11 +719,10 @@ plpgsql_parse_word(char *word)
 	{
 		if (strcmp(cp[0], "tg_argv") == 0)
 		{
-			int			save_spacescanned = plpgsql_SpaceScanned;
+			bool save_spacescanned = plpgsql_SpaceScanned;
 			PLpgSQL_trigarg *trigarg;
 
-			trigarg = malloc(sizeof(PLpgSQL_trigarg));
-			memset(trigarg, 0, sizeof(PLpgSQL_trigarg));
+			trigarg = palloc0(sizeof(PLpgSQL_trigarg));
 			trigarg->dtype = PLPGSQL_DTYPE_TRIGARG;
 
 			if (plpgsql_yylex() != '[')
@@ -851,9 +844,9 @@ plpgsql_parse_dblword(char *word)
 				 */
 				PLpgSQL_recfield *new;
 
-				new = malloc(sizeof(PLpgSQL_recfield));
+				new = palloc(sizeof(PLpgSQL_recfield));
 				new->dtype = PLPGSQL_DTYPE_RECFIELD;
-				new->fieldname = strdup(cp[1]);
+				new->fieldname = pstrdup(cp[1]);
 				new->recparentno = ns->itemno;
 
 				plpgsql_adddatum((PLpgSQL_datum *) new);
@@ -957,9 +950,9 @@ plpgsql_parse_tripword(char *word)
 				 */
 				PLpgSQL_recfield *new;
 
-				new = malloc(sizeof(PLpgSQL_recfield));
+				new = palloc(sizeof(PLpgSQL_recfield));
 				new->dtype = PLPGSQL_DTYPE_RECFIELD;
-				new->fieldname = strdup(cp[2]);
+				new->fieldname = pstrdup(cp[2]);
 				new->recparentno = ns->itemno;
 
 				plpgsql_adddatum((PLpgSQL_datum *) new);
@@ -1038,7 +1031,7 @@ plpgsql_parse_wordtype(char *word)
 	pfree(cp[1]);
 
 	/*
-	 * Do a lookup on the compilers namestack. But ensure it moves up to
+	 * Do a lookup on the compiler's namestack. But ensure it moves up to
 	 * the toplevel.
 	 */
 	old_nsstate = plpgsql_ns_setlocal(false);
@@ -1112,13 +1105,18 @@ plpgsql_parse_dblwordtype(char *word)
 	PLpgSQL_nsitem *nse;
 	bool		old_nsstate;
 	Oid			classOid;
-	HeapTuple	classtup;
+	HeapTuple	classtup = NULL;
+	HeapTuple	attrtup = NULL;
+	HeapTuple	typetup = NULL;
 	Form_pg_class classStruct;
-	HeapTuple	attrtup;
 	Form_pg_attribute attrStruct;
-	HeapTuple	typetup;
 	char	   *cp[3];
 	int			i;
+	MemoryContext oldCxt;
+	int			result = T_ERROR;
+
+	/* Avoid memory leaks in the long-term function context */
+	oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
 
 	/* Do case conversion and word separation */
 	/* We convert %type to .type momentarily to keep converter happy */
@@ -1127,7 +1125,6 @@ plpgsql_parse_dblwordtype(char *word)
 	word[i] = '.';
 	plpgsql_convert_ident(word, cp, 3);
 	word[i] = '%';
-	pfree(cp[2]);
 
 	/*
 	 * Lookup the first word
@@ -1135,8 +1132,8 @@ plpgsql_parse_dblwordtype(char *word)
 	nse = plpgsql_ns_lookup(cp[0], NULL);
 
 	/*
-	 * If this is a label lookup the second word in that labels namestack
-	 * level
+	 * If this is a label lookup the second word in that label's
+	 * namestack level
 	 */
 	if (nse != NULL)
 	{
@@ -1146,26 +1143,15 @@ plpgsql_parse_dblwordtype(char *word)
 			nse = plpgsql_ns_lookup(cp[1], cp[0]);
 			plpgsql_ns_setlocal(old_nsstate);
 
-			pfree(cp[0]);
-			pfree(cp[1]);
-
-			if (nse != NULL)
+			if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
 			{
-				switch (nse->itemtype)
-				{
-					case PLPGSQL_NSTYPE_VAR:
-						plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
-						return T_DTYPE;
-
-					default:
-						return T_ERROR;
-				}
+				plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
+				result = T_DTYPE;
 			}
-			return T_ERROR;
 		}
-		pfree(cp[0]);
-		pfree(cp[1]);
-		return T_ERROR;
+
+		/* Return T_ERROR if not found, otherwise T_DTYPE */
+		goto done;
 	}
 
 	/*
@@ -1173,20 +1159,13 @@ plpgsql_parse_dblwordtype(char *word)
 	 */
 	classOid = RelnameGetRelid(cp[0]);
 	if (!OidIsValid(classOid))
-	{
-		pfree(cp[0]);
-		pfree(cp[1]);
-		return T_ERROR;
-	}
+		goto done;
+
 	classtup = SearchSysCache(RELOID,
 							  ObjectIdGetDatum(classOid),
 							  0, 0, 0);
 	if (!HeapTupleIsValid(classtup))
-	{
-		pfree(cp[0]);
-		pfree(cp[1]);
-		return T_ERROR;
-	}
+		goto done;
 
 	/*
 	 * It must be a relation, sequence, view, or type
@@ -1196,26 +1175,16 @@ plpgsql_parse_dblwordtype(char *word)
 		classStruct->relkind != RELKIND_SEQUENCE &&
 		classStruct->relkind != RELKIND_VIEW &&
 		classStruct->relkind != RELKIND_COMPOSITE_TYPE)
-	{
-		ReleaseSysCache(classtup);
-		pfree(cp[0]);
-		pfree(cp[1]);
-		return T_ERROR;
-	}
+		goto done;
 
 	/*
 	 * Fetch the named table field and it's type
 	 */
 	attrtup = SearchSysCacheAttName(classOid, cp[1]);
 	if (!HeapTupleIsValid(attrtup))
-	{
-		ReleaseSysCache(classtup);
-		pfree(cp[0]);
-		pfree(cp[1]);
-		return T_ERROR;
-	}
-	attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
+		goto done;
 
+	attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
 	typetup = SearchSysCache(TYPEOID,
 							 ObjectIdGetDatum(attrStruct->atttypid),
 							 0, 0, 0);
@@ -1223,16 +1192,24 @@ plpgsql_parse_dblwordtype(char *word)
 		elog(ERROR, "cache lookup failed for type %u", attrStruct->atttypid);
 
 	/*
-	 * Found that - build a compiler type struct and return it
+	 * Found that - build a compiler type struct in the caller's cxt
+	 * and return it
 	 */
+	MemoryContextSwitchTo(oldCxt);
 	plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod);
+	MemoryContextSwitchTo(compile_tmp_cxt);
+	result = T_DTYPE;
 
-	ReleaseSysCache(classtup);
-	ReleaseSysCache(attrtup);
-	ReleaseSysCache(typetup);
-	pfree(cp[0]);
-	pfree(cp[1]);
-	return T_DTYPE;
+done:
+	if (HeapTupleIsValid(classtup))
+		ReleaseSysCache(classtup);
+	if (HeapTupleIsValid(attrtup))
+		ReleaseSysCache(attrtup);
+	if (HeapTupleIsValid(typetup))
+		ReleaseSysCache(typetup);
+
+	MemoryContextSwitchTo(oldCxt);
+	return result;
 }
 
 /* ----------
@@ -1245,17 +1222,22 @@ int
 plpgsql_parse_tripwordtype(char *word)
 {
 	Oid			classOid;
-	HeapTuple	classtup;
+	HeapTuple	classtup = NULL;
 	Form_pg_class classStruct;
-	HeapTuple	attrtup;
+	HeapTuple	attrtup = NULL;
 	Form_pg_attribute attrStruct;
-	HeapTuple	typetup;
+	HeapTuple	typetup = NULL;
 	char	   *cp[2];
 	char	   *colname[1];
 	int			qualified_att_len;
 	int			numdots = 0;
 	int			i;
 	RangeVar   *relvar;
+	MemoryContext oldCxt;
+	int result = T_ERROR;
+
+	/* Avoid memory leaks in the long-term function context */
+	oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
 
 	/* Do case conversion and word separation */
 	qualified_att_len = strlen(word) - TYPE_JUNK_LEN;
@@ -1284,20 +1266,13 @@ plpgsql_parse_tripwordtype(char *word)
 	relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp[0], "plpgsql_parse_tripwordtype"));
 	classOid = RangeVarGetRelid(relvar, true);
 	if (!OidIsValid(classOid))
-	{
-		pfree(cp[0]);
-		pfree(cp[1]);
-		return T_ERROR;
-	}
+		goto done;
+
 	classtup = SearchSysCache(RELOID,
 							  ObjectIdGetDatum(classOid),
 							  0, 0, 0);
 	if (!HeapTupleIsValid(classtup))
-	{
-		pfree(cp[0]);
-		pfree(cp[1]);
-		return T_ERROR;
-	}
+		goto done;
 
 	/*
 	 * It must be a relation, sequence, view, or type
@@ -1307,29 +1282,17 @@ plpgsql_parse_tripwordtype(char *word)
 		classStruct->relkind != RELKIND_SEQUENCE &&
 		classStruct->relkind != RELKIND_VIEW &&
 		classStruct->relkind != RELKIND_COMPOSITE_TYPE)
-	{
-		ReleaseSysCache(classtup);
-		pfree(cp[0]);
-		pfree(cp[1]);
-		return T_ERROR;
-	}
+		goto done;
 
 	/*
 	 * Fetch the named table field and it's type
 	 */
 	plpgsql_convert_ident(cp[1], colname, 1);
 	attrtup = SearchSysCacheAttName(classOid, colname[0]);
-	pfree(colname[0]);
-
 	if (!HeapTupleIsValid(attrtup))
-	{
-		ReleaseSysCache(classtup);
-		pfree(cp[0]);
-		pfree(cp[1]);
-		return T_ERROR;
-	}
-	attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
+		goto done;
 
+	attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
 	typetup = SearchSysCache(TYPEOID,
 							 ObjectIdGetDatum(attrStruct->atttypid),
 							 0, 0, 0);
@@ -1337,17 +1300,24 @@ plpgsql_parse_tripwordtype(char *word)
 		elog(ERROR, "cache lookup failed for type %u", attrStruct->atttypid);
 
 	/*
-	 * Found that - build a compiler type struct and return it
+	 * Found that - build a compiler type struct in the caller's cxt
+	 * and return it
 	 */
+	MemoryContextSwitchTo(oldCxt);
 	plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod);
+	MemoryContextSwitchTo(compile_tmp_cxt);
+	result = T_DTYPE;
 
-	ReleaseSysCache(classtup);
-	ReleaseSysCache(attrtup);
-	ReleaseSysCache(typetup);
-	pfree(cp[0]);
-	pfree(cp[1]);
+done:
+	if (HeapTupleIsValid(classtup))
+		ReleaseSysCache(classtup);
+	if (HeapTupleIsValid(classtup))
+		ReleaseSysCache(attrtup);
+	if (HeapTupleIsValid(typetup))
+		ReleaseSysCache(typetup);
 
-	return T_DTYPE;
+	MemoryContextSwitchTo(oldCxt);
+	return result;
 }
 
 /* ----------
@@ -1403,15 +1373,18 @@ plpgsql_parse_dblwordrowtype(char *word)
 	char	   *cp;
 	int			i;
 	RangeVar   *relvar;
+	MemoryContext oldCxt;
+
+	/* Avoid memory leaks in long-term function context */
+	oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
 
 	/* Do case conversion and word separation */
 	/* We convert %rowtype to .rowtype momentarily to keep converter happy */
 	i = strlen(word) - ROWTYPE_JUNK_LEN;
 	Assert(word[i] == '%');
-
-	cp = (char *) palloc((i + 1) * sizeof(char));
-	memset(cp, 0, (i + 1) * sizeof(char));
-	memcpy(cp, word, i * sizeof(char));
+	word[i] = '\0';
+	cp = pstrdup(word);
+	word[i] = '%';
 
 	/* Lookup the relation */
 	relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp, "plpgsql_parse_dblwordrowtype"));
@@ -1421,26 +1394,25 @@ plpgsql_parse_dblwordrowtype(char *word)
 				(errcode(ERRCODE_UNDEFINED_TABLE),
 				 errmsg("relation \"%s\" does not exist", cp)));
 
-	/*
-	 * Build and return the row type struct
-	 */
+	/* Build and return the row type struct */
 	plpgsql_yylval.dtype = plpgsql_build_datatype(get_rel_type_id(classOid),
 												  -1);
 
-	pfree(cp);
-
+	MemoryContextSwitchTo(oldCxt);
 	return T_DTYPE;
 }
 
 /*
- * plpgsql_build_variable - build a datum-array entry of a given datatype
+ * plpgsql_build_variable - build a datum-array entry of a given
+ * datatype
  *
- * The returned struct may be a PLpgSQL_var, PLpgSQL_row, or PLpgSQL_rec
- * depending on the given datatype.  The struct is automatically added
- * to the current datum array, and optionally to the current namespace.
+ * The returned struct may be a PLpgSQL_var, PLpgSQL_row, or
+ * PLpgSQL_rec depending on the given datatype, and is allocated via
+ * palloc.  The struct is automatically added to the current datum
+ * array, and optionally to the current namespace.
  */
 PLpgSQL_variable *
-plpgsql_build_variable(char *refname, int lineno, PLpgSQL_type *dtype,
+plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
 					   bool add2namespace)
 {
 	PLpgSQL_variable *result;
@@ -1452,11 +1424,9 @@ plpgsql_build_variable(char *refname, int lineno, PLpgSQL_type *dtype,
 				/* Ordinary scalar datatype */
 				PLpgSQL_var *var;
 
-				var = malloc(sizeof(PLpgSQL_var));
-				memset(var, 0, sizeof(PLpgSQL_var));
-
+				var = palloc0(sizeof(PLpgSQL_var));
 				var->dtype = PLPGSQL_DTYPE_VAR;
-				var->refname = refname;
+				var->refname = pstrdup(refname);
 				var->lineno = lineno;
 				var->datatype = dtype;
 				/* other fields might be filled by caller */
@@ -1482,7 +1452,7 @@ plpgsql_build_variable(char *refname, int lineno, PLpgSQL_type *dtype,
 				row = build_row_var(dtype->typrelid);
 
 				row->dtype = PLPGSQL_DTYPE_ROW;
-				row->refname = refname;
+				row->refname = pstrdup(refname);
 				row->lineno = lineno;
 
 				plpgsql_adddatum((PLpgSQL_datum *) row);
@@ -1501,11 +1471,9 @@ plpgsql_build_variable(char *refname, int lineno, PLpgSQL_type *dtype,
 				 */
 				PLpgSQL_rec *rec;
 
-				rec = malloc(sizeof(PLpgSQL_rec));
-				memset(rec, 0, sizeof(PLpgSQL_rec));
-
+				rec = palloc0(sizeof(PLpgSQL_rec));
 				rec->dtype = PLPGSQL_DTYPE_REC;
-				rec->refname = refname;
+				rec->refname = pstrdup(refname);
 				rec->lineno = lineno;
 
 				plpgsql_adddatum((PLpgSQL_datum *) rec);
@@ -1517,14 +1485,12 @@ plpgsql_build_variable(char *refname, int lineno, PLpgSQL_type *dtype,
 				break;
 			}
 		case PLPGSQL_TTYPE_PSEUDO:
-			{
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("variable \"%s\" has pseudo-type %s",
-								refname, format_type_be(dtype->typoid))));
-				result = NULL;	/* keep compiler quiet */
-				break;
-			}
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("variable \"%s\" has pseudo-type %s",
+							refname, format_type_be(dtype->typoid))));
+			result = NULL;	/* keep compiler quiet */
+			break;
 		default:
 			elog(ERROR, "unrecognized ttype: %d", dtype->ttype);
 			result = NULL;		/* keep compiler quiet */
@@ -1545,7 +1511,6 @@ build_row_var(Oid classOid)
 	Form_pg_class classStruct;
 	const char *relname;
 	int			i;
-	MemoryContext oldcxt;
 
 	/*
 	 * Open the relation to get info.
@@ -1567,23 +1532,12 @@ build_row_var(Oid classOid)
 	 * Create a row datum entry and all the required variables that it
 	 * will point to.
 	 */
-	row = malloc(sizeof(PLpgSQL_row));
-	memset(row, 0, sizeof(PLpgSQL_row));
-
+	row = palloc0(sizeof(PLpgSQL_row));
 	row->dtype = PLPGSQL_DTYPE_ROW;
-
-	/*
-	 * This is a bit ugly --- need a permanent copy of the rel's tupdesc.
-	 * Someday all these mallocs should go away in favor of a per-function
-	 * memory context ...
-	 */
-	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
 	row->rowtupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
-	MemoryContextSwitchTo(oldcxt);
-
 	row->nfields = classStruct->relnatts;
-	row->fieldnames = malloc(sizeof(char *) * row->nfields);
-	row->varnos = malloc(sizeof(int) * row->nfields);
+	row->fieldnames = palloc(sizeof(char *) * row->nfields);
+	row->varnos = palloc(sizeof(int) * row->nfields);
 
 	for (i = 0; i < row->nfields; i++)
 	{
@@ -1592,19 +1546,16 @@ build_row_var(Oid classOid)
 		/*
 		 * Get the attribute and check for dropped column
 		 */
-		attrStruct = RelationGetDescr(rel)->attrs[i];
+		attrStruct = row->rowtupdesc->attrs[i];
 
 		if (!attrStruct->attisdropped)
 		{
-			const char *attname;
-			char	   *refname;
+			char	   *attname;
+			char		refname[(NAMEDATALEN * 2) + 100];
 			PLpgSQL_variable *var;
 
 			attname = NameStr(attrStruct->attname);
-			refname = malloc(strlen(relname) + strlen(attname) + 2);
-			strcpy(refname, relname);
-			strcat(refname, ".");
-			strcat(refname, attname);
+			snprintf(refname, sizeof(refname), "%s.%s", relname, attname);
 
 			/*
 			 * Create the internal variable for the field
@@ -1621,10 +1572,8 @@ build_row_var(Oid classOid)
 												  attrStruct->atttypmod),
 										 false);
 
-			/*
-			 * Add the variable to the row.
-			 */
-			row->fieldnames[i] = strdup(attname);
+			/* Add the variable to the row */
+			row->fieldnames[i] = attname;
 			row->varnos[i] = var->dno;
 		}
 		else
@@ -1697,9 +1646,9 @@ build_datatype(HeapTuple typeTup, int32 typmod)
 				 errmsg("type \"%s\" is only a shell",
 						NameStr(typeStruct->typname))));
 
-	typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
+	typ = (PLpgSQL_type *) palloc(sizeof(PLpgSQL_type));
 
-	typ->typname = strdup(NameStr(typeStruct->typname));
+	typ->typname = pstrdup(NameStr(typeStruct->typname));
 	typ->typoid = HeapTupleGetOid(typeTup);
 	switch (typeStruct->typtype)
 	{
@@ -1726,7 +1675,7 @@ build_datatype(HeapTuple typeTup, int32 typmod)
 	typ->typbyval = typeStruct->typbyval;
 	typ->typrelid = typeStruct->typrelid;
 	typ->typioparam = getTypeIOParam(typeTup);
-	perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
+	fmgr_info(typeStruct->typinput, &(typ->typinput));
 	typ->atttypmod = typmod;
 
 	return typ;
@@ -1757,7 +1706,7 @@ plpgsql_parse_err_condition(char *condname)
 	 */
 	if (strcmp(condname, "others") == 0)
 	{
-		new = malloc(sizeof(PLpgSQL_condition));
+		new = palloc(sizeof(PLpgSQL_condition));
 		new->sqlerrstate = 0;
 		new->condname = condname;
 		new->next = NULL;
@@ -1769,7 +1718,7 @@ plpgsql_parse_err_condition(char *condname)
 	{
 		if (strcmp(condname, exception_label_map[i].label) == 0)
 		{
-			new = malloc(sizeof(PLpgSQL_condition));
+			new = palloc(sizeof(PLpgSQL_condition));
 			new->sqlerrstate = exception_label_map[i].sqlerrstate;
 			new->condname = condname;
 			new->next = prev;
@@ -1836,7 +1785,7 @@ plpgsql_add_initdatums(int **varnos)
 	{
 		if (n > 0)
 		{
-			*varnos = (int *) malloc(sizeof(int) * n);
+			*varnos = (int *) palloc(sizeof(int) * n);
 
 			n = 0;
 			for (i = datums_last; i < plpgsql_nDatums; i++)
@@ -1930,12 +1879,30 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
 	}
 }
 
+static void
+delete_function(PLpgSQL_function *func)
+{
+	/* remove function from hash table */
+	plpgsql_HashTableDelete(func);
+
+	/* release the function's storage */
+	MemoryContextDelete(func->fn_cxt);
+
+	/*
+	 * Caller should be sure not to use passed-in pointer, as it now
+	 * points to pfree'd storage
+	 */
+}
+
 /* exported so we can call it from plpgsql_init() */
 void
 plpgsql_HashTableInit(void)
 {
 	HASHCTL		ctl;
 
+	/* don't allow double-initialization */
+	Assert(plpgsql_HashTable == NULL);
+
 	memset(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(PLpgSQL_func_hashkey);
 	ctl.entrysize = sizeof(plpgsql_HashEnt);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 096f22b1d944684c30976872a18e53ee28fc2f8e..b9917db2ca22c9b8745e0562dcc3461816ac4c3e 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.129 2005/02/22 04:43:07 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.130 2005/02/22 07:18:24 neilc Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -74,13 +74,14 @@ static PLpgSQL_expr *active_simple_exprs = NULL;
  * Local function forward declarations
  ************************************************************/
 static void plpgsql_exec_error_callback(void *arg);
+static PLpgSQL_datum *copy_plpgsql_datum(PLpgSQL_datum *datum);
 static PLpgSQL_var *copy_var(PLpgSQL_var *var);
 static PLpgSQL_rec *copy_rec(PLpgSQL_rec *rec);
 
 static int exec_stmt_block(PLpgSQL_execstate *estate,
 				PLpgSQL_stmt_block *block);
 static int exec_stmts(PLpgSQL_execstate *estate,
-		   PLpgSQL_stmts *stmts);
+					  List *stmts);
 static int exec_stmt(PLpgSQL_execstate *estate,
 		  PLpgSQL_stmt *stmt);
 static int exec_stmt_assign(PLpgSQL_execstate *estate,
@@ -212,29 +213,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
 	 */
 	estate.err_text = gettext_noop("during initialization of execution state");
 	for (i = 0; i < func->ndatums; i++)
-	{
-		switch (func->datums[i]->dtype)
-		{
-			case PLPGSQL_DTYPE_VAR:
-				estate.datums[i] = (PLpgSQL_datum *)
-					copy_var((PLpgSQL_var *) (func->datums[i]));
-				break;
-
-			case PLPGSQL_DTYPE_REC:
-				estate.datums[i] = (PLpgSQL_datum *)
-					copy_rec((PLpgSQL_rec *) (func->datums[i]));
-				break;
-
-			case PLPGSQL_DTYPE_ROW:
-			case PLPGSQL_DTYPE_RECFIELD:
-			case PLPGSQL_DTYPE_ARRAYELEM:
-				estate.datums[i] = func->datums[i];
-				break;
-
-			default:
-				elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype);
-		}
-	}
+		estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
 
 	/*
 	 * Store the actual call argument values into the variables
@@ -467,30 +446,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
 	 */
 	estate.err_text = gettext_noop("during initialization of execution state");
 	for (i = 0; i < func->ndatums; i++)
-	{
-		switch (func->datums[i]->dtype)
-		{
-			case PLPGSQL_DTYPE_VAR:
-				estate.datums[i] = (PLpgSQL_datum *)
-					copy_var((PLpgSQL_var *) (func->datums[i]));
-				break;
-
-			case PLPGSQL_DTYPE_REC:
-				estate.datums[i] = (PLpgSQL_datum *)
-					copy_rec((PLpgSQL_rec *) (func->datums[i]));
-				break;
-
-			case PLPGSQL_DTYPE_ROW:
-			case PLPGSQL_DTYPE_RECFIELD:
-			case PLPGSQL_DTYPE_ARRAYELEM:
-			case PLPGSQL_DTYPE_TRIGARG:
-				estate.datums[i] = func->datums[i];
-				break;
-
-			default:
-				elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype);
-		}
-	}
+		estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
 
 	/*
 	 * Put the OLD and NEW tuples into record variables
@@ -758,6 +714,35 @@ plpgsql_exec_error_callback(void *arg)
  * Support functions for copying local execution variables
  * ----------
  */
+static PLpgSQL_datum *
+copy_plpgsql_datum(PLpgSQL_datum *datum)
+{
+	PLpgSQL_datum *result = NULL;
+
+	switch (datum->dtype)
+	{
+		case PLPGSQL_DTYPE_VAR:
+			result = (PLpgSQL_datum *) copy_var((PLpgSQL_var *) datum);
+			break;
+
+		case PLPGSQL_DTYPE_REC:
+			result = (PLpgSQL_datum *) copy_rec((PLpgSQL_rec *) datum);
+			break;
+
+		case PLPGSQL_DTYPE_ROW:
+		case PLPGSQL_DTYPE_RECFIELD:
+		case PLPGSQL_DTYPE_ARRAYELEM:
+		case PLPGSQL_DTYPE_TRIGARG:
+			result = datum;
+			break;
+
+		default:
+			elog(ERROR, "unrecognized dtype: %d", datum->dtype);
+	}
+
+	return result;
+}
+
 static PLpgSQL_var *
 copy_var(PLpgSQL_var *var)
 {
@@ -920,9 +905,8 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
 		}
 		PG_CATCH();
 		{
-			ErrorData  *edata;
-			PLpgSQL_exceptions *exceptions;
-			int			j;
+			ErrorData	*edata;
+			ListCell	*e;
 
 			/* Save error info */
 			MemoryContextSwitchTo(oldcontext);
@@ -942,10 +926,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
 			SPI_restore_connection();
 
 			/* Look for a matching exception handler */
-			exceptions = block->exceptions;
-			for (j = 0; j < exceptions->exceptions_used; j++)
+			foreach (e, block->exceptions)
 			{
-				PLpgSQL_exception *exception = exceptions->exceptions[j];
+				PLpgSQL_exception *exception = (PLpgSQL_exception *) lfirst(e);
 
 				if (exception_matches_conditions(edata, exception->conditions))
 				{
@@ -955,7 +938,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
 			}
 
 			/* If no match found, re-throw the error */
-			if (j >= exceptions->exceptions_used)
+			if (e == NULL)
 				ReThrowError(edata);
 			else
 				FreeErrorData(edata);
@@ -1005,14 +988,14 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
  * ----------
  */
 static int
-exec_stmts(PLpgSQL_execstate *estate, PLpgSQL_stmts *stmts)
+exec_stmts(PLpgSQL_execstate *estate, List *stmts)
 {
-	int			rc;
-	int			i;
+	ListCell   *s;
 
-	for (i = 0; i < stmts->stmts_used; i++)
+	foreach (s, stmts)
 	{
-		rc = exec_stmt(estate, stmts->stmts[i]);
+		PLpgSQL_stmt *stmt = (PLpgSQL_stmt *) lfirst(s);
+		int rc = exec_stmt(estate, stmt);
 		if (rc != PLPGSQL_RC_OK)
 			return rc;
 	}
@@ -1184,23 +1167,23 @@ exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt)
 static int
 exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
 {
-	int			i;
-	PLpgSQL_datum *var;
-	bool		isnull = false;
+	ListCell *lc;
 
-	for (i = 0; i < stmt->ndtitems; i++)
+	foreach (lc, stmt->diag_items)
 	{
-		PLpgSQL_diag_item *dtitem = &stmt->dtitems[i];
+		PLpgSQL_diag_item	*diag_item = (PLpgSQL_diag_item *) lfirst(lc);
+		PLpgSQL_datum		*var;
+		bool				 isnull = false;
 
-		if (dtitem->target <= 0)
+		if (diag_item->target <= 0)
 			continue;
 
-		var = (estate->datums[dtitem->target]);
+		var = estate->datums[diag_item->target];
 
 		if (var == NULL)
 			continue;
 
-		switch (dtitem->item)
+		switch (diag_item->kind)
 		{
 			case PLPGSQL_GETDIAG_ROW_COUNT:
 
@@ -1218,7 +1201,7 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
 
 			default:
 				elog(ERROR, "unrecognized attribute request: %d",
-					 dtitem->item);
+					 diag_item->kind);
 		}
 	}
 
@@ -1242,12 +1225,12 @@ exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt)
 
 	if (!isnull && value)
 	{
-		if (stmt->true_body != NULL)
+		if (stmt->true_body != NIL)
 			return exec_stmts(estate, stmt->true_body);
 	}
 	else
 	{
-		if (stmt->false_body != NULL)
+		if (stmt->false_body != NIL)
 			return exec_stmts(estate, stmt->false_body);
 	}
 
@@ -1749,6 +1732,7 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
 	if (estate->fn_rettype == VOIDOID)
 	{
 		/* Special hack for function returning VOID */
+		Assert(stmt->expr == NULL);
 		estate->retval = (Datum) 0;
 		estate->retisnull = false;
 		estate->rettype = VOIDOID;
@@ -1903,38 +1887,39 @@ exec_init_tuple_store(PLpgSQL_execstate *estate)
 static int
 exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
 {
-	Oid			paramtypeid;
-	Datum		paramvalue;
-	bool		paramisnull;
-	char	   *extval;
-	int			pidx = 0;
-	char		c[2] = {0, 0};
 	char	   *cp;
 	PLpgSQL_dstring ds;
+	ListCell   *current_param;
 
 	plpgsql_dstring_init(&ds);
+	current_param = list_head(stmt->params);
 
 	for (cp = stmt->message; *cp; cp++)
 	{
 		/*
-		 * Occurrences of a single % are replaced by the next argument's
+		 * Occurrences of a single % are replaced by the next parameter's
 		 * external representation. Double %'s are converted to one %.
 		 */
-		if ((c[0] = *cp) == '%')
+		if (cp[0] == '%')
 		{
-			cp++;
-			if (*cp == '%')
-			{
-				plpgsql_dstring_append(&ds, c);
-				continue;
-			}
-			cp--;
-			if (pidx >= stmt->nparams)
+			Oid			paramtypeid;
+			Datum		paramvalue;
+			bool		paramisnull;
+			char	   *extval;
+
+			if (cp[1] == '%')
 			{
-				plpgsql_dstring_append(&ds, c);
+				plpgsql_dstring_append_char(&ds, cp[1]);
+				cp++;
 				continue;
 			}
-			exec_eval_datum(estate, estate->datums[stmt->params[pidx]],
+
+			if (current_param == NULL)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("too few parameters specified for RAISE")));
+
+			exec_eval_datum(estate, estate->datums[lfirst_int(current_param)],
 							InvalidOid,
 							&paramtypeid, &paramvalue, &paramisnull);
 			if (paramisnull)
@@ -1942,13 +1927,22 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
 			else
 				extval = convert_value_to_string(paramvalue, paramtypeid);
 			plpgsql_dstring_append(&ds, extval);
-			pidx++;
+			current_param = lnext(current_param);
 			continue;
 		}
 
-		plpgsql_dstring_append(&ds, c);
+		plpgsql_dstring_append_char(&ds, cp[0]);
 	}
 
+	/*
+	 * If more parameters were specified than were required to process
+	 * the format string, throw an error
+	 */
+	if (current_param != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("too many parameters specified for RAISE")));
+
 	/*
 	 * Throw the error (may or may not come back)
 	 */
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index 555d0d652d5f797cf942473d95555ae5394f86ae..91d7e1558e3735d53e8c799c14bbffbbd11323e0 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.38 2004/10/10 23:37:45 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.39 2005/02/22 07:18:24 neilc Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -59,7 +59,7 @@ void
 plpgsql_dstring_init(PLpgSQL_dstring *ds)
 {
 	ds->value = palloc(ds->alloc = 512);
-	ds->used = 0;
+	ds->used = 1;
 	ds->value[0] = '\0';
 }
 
@@ -74,6 +74,20 @@ plpgsql_dstring_free(PLpgSQL_dstring *ds)
 	pfree(ds->value);
 }
 
+static void
+plpgsql_dstring_expand(PLpgSQL_dstring *ds, int needed)
+{
+	/* Don't allow truncating the string */
+	Assert(needed > ds->alloc);
+	Assert(ds->used <= ds->alloc);
+
+	/* Might have to double more than once, if needed is large */
+	do
+	{
+		ds->alloc *= 2;
+	} while (needed > ds->alloc);
+	ds->value = repalloc(ds->value, ds->alloc);
+}
 
 /* ----------
  * plpgsql_dstring_append		Dynamic string extending
@@ -83,20 +97,30 @@ void
 plpgsql_dstring_append(PLpgSQL_dstring *ds, const char *str)
 {
 	int			len = strlen(str);
-	int			needed = ds->used + len + 1;
+	int			needed = ds->used + len;
 
 	if (needed > ds->alloc)
-	{
-		/* might have to double more than once, if len is large */
-		do
-		{
-			ds->alloc *= 2;
-		} while (needed > ds->alloc);
-		ds->value = repalloc(ds->value, ds->alloc);
-	}
+		plpgsql_dstring_expand(ds, needed);
 
-	strcpy(&(ds->value[ds->used]), str);
+	memcpy(&(ds->value[ds->used - 1]), str, len);
 	ds->used += len;
+	ds->value[ds->used - 1] = '\0';
+}
+
+/* ----------
+ * plpgsql_dstring_append_char	Append a single character
+ *								to a dynamic string
+ * ----------
+ */
+void
+plpgsql_dstring_append_char(PLpgSQL_dstring *ds, char c)
+{
+	if (ds->used == ds->alloc)
+		plpgsql_dstring_expand(ds, ds->used + 1);
+
+	ds->value[ds->used - 1] = c;
+	ds->value[ds->used] = '\0';
+	ds->used++;
 }
 
 
@@ -187,7 +211,7 @@ plpgsql_ns_pop(void)
  * ----------
  */
 void
-plpgsql_ns_additem(int itemtype, int itemno, char *name)
+plpgsql_ns_additem(int itemtype, int itemno, const char *name)
 {
 	PLpgSQL_ns *ns = ns_current;
 	PLpgSQL_nsitem *nse;
@@ -286,11 +310,8 @@ plpgsql_ns_rename(char *oldname, char *newname)
 	int			i;
 
 	/*
-	 * Lookup in the current namespace only
-	 */
-
-	/*
-	 * Lookup name in the namestack
+	 * Lookup name in the namestack; do the lookup in the current
+	 * namespace only.
 	 */
 	for (ns = ns_current; ns != NULL; ns = ns->upper)
 	{
@@ -584,20 +605,19 @@ dump_stmt(PLpgSQL_stmt *stmt)
 }
 
 static void
-dump_stmts(PLpgSQL_stmts *stmts)
+dump_stmts(List *stmts)
 {
-	int			i;
+	ListCell *s;
 
 	dump_indent += 2;
-	for (i = 0; i < stmts->stmts_used; i++)
-		dump_stmt(stmts->stmts[i]);
+	foreach (s, stmts)
+		dump_stmt((PLpgSQL_stmt *) lfirst(s));
 	dump_indent -= 2;
 }
 
 static void
 dump_block(PLpgSQL_stmt_block *block)
 {
-	int			i;
 	char	   *name;
 
 	if (block->label == NULL)
@@ -612,9 +632,11 @@ dump_block(PLpgSQL_stmt_block *block)
 
 	if (block->exceptions)
 	{
-		for (i = 0; i < block->exceptions->exceptions_used; i++)
+		ListCell *e;
+
+		foreach (e, block->exceptions)
 		{
-			PLpgSQL_exception *exc = block->exceptions->exceptions[i];
+			PLpgSQL_exception *exc = (PLpgSQL_exception *) lfirst(e);
 			PLpgSQL_condition *cond;
 
 			dump_ind();
@@ -863,12 +885,12 @@ dump_return_next(PLpgSQL_stmt_return_next *stmt)
 static void
 dump_raise(PLpgSQL_stmt_raise *stmt)
 {
-	int			i;
+	ListCell *l;
 
 	dump_ind();
 	printf("RAISE '%s'", stmt->message);
-	for (i = 0; i < stmt->nparams; i++)
-		printf(" %d", stmt->params[i]);
+	foreach (l, stmt->params)
+		printf(" %d", lfirst_int(l));
 	printf("\n");
 }
 
@@ -907,20 +929,20 @@ dump_dynfors(PLpgSQL_stmt_dynfors *stmt)
 static void
 dump_getdiag(PLpgSQL_stmt_getdiag *stmt)
 {
-	int			i;
+	ListCell *lc;
 
 	dump_ind();
 	printf("GET DIAGNOSTICS ");
-	for (i = 0; i < stmt->ndtitems; i++)
+	foreach (lc, stmt->diag_items)
 	{
-		PLpgSQL_diag_item *dtitem = &stmt->dtitems[i];
+		PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
 
-		if (i != 0)
+		if (lc != list_head(stmt->diag_items))
 			printf(", ");
 
-		printf("{var %d} = ", dtitem->target);
+		printf("{var %d} = ", diag_item->target);
 
-		switch (dtitem->item)
+		switch (diag_item->kind)
 		{
 			case PLPGSQL_GETDIAG_ROW_COUNT:
 				printf("ROW_COUNT");
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index d4e892eb719e2465ea9957b5e41948d0f48e62ed..06e73429169144028ec607d4126d76c42ff86a0a 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.23 2004/08/01 17:32:22 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.24 2005/02/22 07:18:24 neilc Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -47,7 +47,7 @@
 
 extern DLLIMPORT bool check_function_bodies;
 
-static int	plpgsql_firstcall = 1;
+static bool	plpgsql_firstcall = true;
 
 static void plpgsql_init_all(void);
 
@@ -65,10 +65,8 @@ plpgsql_init(void)
 		return;
 
 	plpgsql_HashTableInit();
-
 	RegisterXactCallback(plpgsql_xact_cb, NULL);
-
-	plpgsql_firstcall = 0;
+	plpgsql_firstcall = false;
 }
 
 /*
@@ -78,14 +76,12 @@ static void
 plpgsql_init_all(void)
 {
 	/* Execute any postmaster-startup safe initialization */
-	if (plpgsql_firstcall)
-		plpgsql_init();
+	plpgsql_init();
 
 	/*
 	 * Any other initialization that must be done each time a new backend
 	 * starts -- currently none
 	 */
-
 }
 
 /* ----------
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index b38b08bce4b7d7664b082556d735d5524cbf80af..df38351a23eb01c7ef55a305b70141439463eb32 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.56 2004/09/16 16:58:44 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.57 2005/02/22 07:18:24 neilc Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -50,7 +50,7 @@
  **********************************************************************/
 
 /* ----------
- * Compilers namestack item types
+ * Compiler's namestack item types
  * ----------
  */
 enum
@@ -147,7 +147,7 @@ enum
 typedef struct
 {								/* Dynamic string control structure */
 	int			alloc;
-	int			used;
+	int			used;			/* Including NUL terminator */
 	char	   *value;
 } PLpgSQL_dstring;
 
@@ -298,6 +298,7 @@ typedef struct
 } PLpgSQL_nsitem;
 
 
+/* XXX: consider adapting this to use List */
 typedef struct PLpgSQL_ns
 {								/* Compiler namestack level		*/
 	int			items_alloc;
@@ -314,14 +315,6 @@ typedef struct
 } PLpgSQL_stmt;
 
 
-typedef struct
-{								/* List of execution nodes		*/
-	int			stmts_alloc;	/* XXX this oughta just be a List ... */
-	int			stmts_used;
-	PLpgSQL_stmt **stmts;
-} PLpgSQL_stmts;
-
-
 typedef struct PLpgSQL_condition
 {								/* One EXCEPTION condition name */
 	int			sqlerrstate;	/* SQLSTATE code */
@@ -333,26 +326,17 @@ typedef struct
 {								/* One EXCEPTION ... WHEN clause */
 	int			lineno;
 	PLpgSQL_condition *conditions;
-	PLpgSQL_stmts *action;
+	List	   *action;			/* List of statements */
 } PLpgSQL_exception;
 
 
-typedef struct
-{								/* List of WHEN clauses			*/
-	int			exceptions_alloc;		/* XXX this oughta just be a List
-										 * ... */
-	int			exceptions_used;
-	PLpgSQL_exception **exceptions;
-} PLpgSQL_exceptions;
-
-
 typedef struct
 {								/* Block of statements			*/
 	int			cmd_type;
 	int			lineno;
 	char	   *label;
-	PLpgSQL_stmts *body;
-	PLpgSQL_exceptions *exceptions;
+	List	   *body;			/* List of statements */
+	List	   *exceptions;		/* List of WHEN clauses */
 	int			n_initvars;
 	int		   *initvarnos;
 } PLpgSQL_stmt_block;
@@ -375,7 +359,7 @@ typedef struct
 
 typedef struct
 {								/* Get Diagnostics item		*/
-	int			item;			/* id for diagnostic value desired */
+	int			kind;			/* id for diagnostic value desired */
 	int			target;			/* where to assign it */
 } PLpgSQL_diag_item;
 
@@ -383,8 +367,7 @@ typedef struct
 {								/* Get Diagnostics statement		*/
 	int			cmd_type;
 	int			lineno;
-	int			ndtitems;
-	PLpgSQL_diag_item *dtitems;
+	List	   *diag_items;		/* List of PLpgSQL_diag_item */
 } PLpgSQL_stmt_getdiag;
 
 
@@ -393,8 +376,8 @@ typedef struct
 	int			cmd_type;
 	int			lineno;
 	PLpgSQL_expr *cond;
-	PLpgSQL_stmts *true_body;
-	PLpgSQL_stmts *false_body;
+	List	   *true_body;		/* List of statements */
+	List	   *false_body;		/* List of statements */
 } PLpgSQL_stmt_if;
 
 
@@ -403,7 +386,7 @@ typedef struct
 	int			cmd_type;
 	int			lineno;
 	char	   *label;
-	PLpgSQL_stmts *body;
+	List	   *body;			/* List of statements */
 } PLpgSQL_stmt_loop;
 
 
@@ -413,7 +396,7 @@ typedef struct
 	int			lineno;
 	char	   *label;
 	PLpgSQL_expr *cond;
-	PLpgSQL_stmts *body;
+	List	   *body;			/* List of statements */
 } PLpgSQL_stmt_while;
 
 
@@ -426,7 +409,7 @@ typedef struct
 	PLpgSQL_expr *lower;
 	PLpgSQL_expr *upper;
 	int			reverse;
-	PLpgSQL_stmts *body;
+	List	   *body;			/* List of statements */
 } PLpgSQL_stmt_fori;
 
 
@@ -438,7 +421,7 @@ typedef struct
 	PLpgSQL_rec *rec;
 	PLpgSQL_row *row;
 	PLpgSQL_expr *query;
-	PLpgSQL_stmts *body;
+	List	   *body;			/* List of statements */
 } PLpgSQL_stmt_fors;
 
 
@@ -450,7 +433,7 @@ typedef struct
 	PLpgSQL_rec *rec;
 	PLpgSQL_row *row;
 	PLpgSQL_expr *query;
-	PLpgSQL_stmts *body;
+	List	   *body;			/* List of statements */
 } PLpgSQL_stmt_dynfors;
 
 
@@ -527,8 +510,7 @@ typedef struct
 	int			lineno;
 	int			elog_level;
 	char	   *message;
-	int			nparams;
-	int		   *params;
+	List	   *params;
 } PLpgSQL_stmt_raise;
 
 
@@ -577,6 +559,7 @@ typedef struct PLpgSQL_function
 	CommandId	fn_cmin;
 	int			fn_functype;
 	PLpgSQL_func_hashkey *fn_hashkey;	/* back-link to hashtable key */
+	MemoryContext fn_cxt;
 
 	Oid			fn_rettype;
 	int			fn_rettyplen;
@@ -649,8 +632,8 @@ typedef struct
  * Global variable declarations
  **********************************************************************/
 
-extern int	plpgsql_DumpExecTree;
-extern int	plpgsql_SpaceScanned;
+extern bool	plpgsql_DumpExecTree;
+extern bool	plpgsql_SpaceScanned;
 extern int	plpgsql_nDatums;
 extern PLpgSQL_datum **plpgsql_Datums;
 
@@ -663,6 +646,8 @@ extern char *plpgsql_base_yytext;
 #define plpgsql_yytext plpgsql_base_yytext
 
 extern PLpgSQL_function *plpgsql_curr_compile;
+extern bool		plpgsql_check_syntax;
+extern MemoryContext compile_tmp_cxt;
 
 /**********************************************************************
  * Function declarations
@@ -684,13 +669,14 @@ extern int	plpgsql_parse_wordrowtype(char *word);
 extern int	plpgsql_parse_dblwordrowtype(char *word);
 extern PLpgSQL_type *plpgsql_parse_datatype(const char *string);
 extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod);
-extern PLpgSQL_variable *plpgsql_build_variable(char *refname, int lineno,
+extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
 					   PLpgSQL_type *dtype,
 					   bool add2namespace);
 extern PLpgSQL_condition *plpgsql_parse_err_condition(char *condname);
 extern void plpgsql_adddatum(PLpgSQL_datum *new);
 extern int	plpgsql_add_initdatums(int **varnos);
 extern void plpgsql_HashTableInit(void);
+extern void plpgsql_compile_error_callback(void *arg);
 
 /* ----------
  * Functions in pl_handler.c
@@ -717,6 +703,7 @@ extern void plpgsql_xact_cb(XactEvent event, void *arg);
 extern void plpgsql_dstring_init(PLpgSQL_dstring *ds);
 extern void plpgsql_dstring_free(PLpgSQL_dstring *ds);
 extern void plpgsql_dstring_append(PLpgSQL_dstring *ds, const char *str);
+extern void plpgsql_dstring_append_char(PLpgSQL_dstring *ds, char c);
 extern char *plpgsql_dstring_get(PLpgSQL_dstring *ds);
 
 /* ----------
@@ -727,7 +714,7 @@ extern void plpgsql_ns_init(void);
 extern bool plpgsql_ns_setlocal(bool flag);
 extern void plpgsql_ns_push(char *label);
 extern void plpgsql_ns_pop(void);
-extern void plpgsql_ns_additem(int itemtype, int itemno, char *name);
+extern void plpgsql_ns_additem(int itemtype, int itemno, const char *name);
 extern PLpgSQL_nsitem *plpgsql_ns_lookup(char *name, char *nsname);
 extern void plpgsql_ns_rename(char *oldname, char *newname);
 
diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l
index 5e92ba76905c5ab49d2df9d19f255053f56dceb7..b4643d9e618ca4468f4b1eee3b0f737f6663b165 100644
--- a/src/pl/plpgsql/src/scan.l
+++ b/src/pl/plpgsql/src/scan.l
@@ -4,7 +4,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *    $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.38 2004/12/17 03:51:36 neilc Exp $
+ *    $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.39 2005/02/22 07:18:24 neilc Exp $
  *
  *    This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -54,7 +54,7 @@ static char *scanbuf;
 static const char *scanstr;		/* original input string */
 
 static int	scanner_functype;
-static int	scanner_typereported;
+static bool	scanner_typereported;
 static int	pushback_token;
 static bool have_pushback_token;
 static int	lookahead_token;	
@@ -64,7 +64,7 @@ static int	cur_line_num;
 static char    *dolqstart;      /* current $foo$ quote start string */
 static int	dolqlen;			/* signal to plpgsql_get_string_value */
 
-int	plpgsql_SpaceScanned = 0;
+bool plpgsql_SpaceScanned = false;
 %}
 
 %option 8bit
@@ -114,7 +114,7 @@ dolqinside		[^$]+
      * ----------
      */
     BEGIN(INITIAL);
-    plpgsql_SpaceScanned = 0;
+    plpgsql_SpaceScanned = false;
 
     /* ----------
      * On the first call to a new source report the
@@ -123,7 +123,7 @@ dolqinside		[^$]+
      */
 	if (!scanner_typereported)
 	{
-		scanner_typereported = 1;
+		scanner_typereported = true;
 		return scanner_functype;
 	}
 
@@ -255,7 +255,7 @@ dump			{ return O_DUMP;			}
      * Ignore whitespaces but remember this happened
      * ----------
      */
-{space}+		{ plpgsql_SpaceScanned = 1;		}
+{space}+		{ plpgsql_SpaceScanned = true;		}
 
     /* ----------
      * Eat up comments
@@ -266,7 +266,7 @@ dump			{ return O_DUMP;			}
 \/\*			{ start_lineno = plpgsql_scanner_lineno();
 			  BEGIN(IN_COMMENT);
 			}
-<IN_COMMENT>\*\/	{ BEGIN(INITIAL); plpgsql_SpaceScanned = 1; }
+<IN_COMMENT>\*\/	{ BEGIN(INITIAL); plpgsql_SpaceScanned = true; }
 <IN_COMMENT>\n		;
 <IN_COMMENT>.		;
 <IN_COMMENT><<EOF>>	{
@@ -502,7 +502,7 @@ plpgsql_scanner_init(const char *str, int functype)
 	scanstr = str;
 
     scanner_functype = functype;
-    scanner_typereported = 0;
+    scanner_typereported = false;
 
 	have_pushback_token = false;
 	have_lookahead_token = false;
@@ -538,7 +538,7 @@ plpgsql_scanner_finish(void)
 
 /*
  * Called after a T_STRING token is read to get the string literal's value
- * as a malloc'd string.  (We make this a separate call because in many
+ * as a palloc'd string.  (We make this a separate call because in many
  * scenarios there's no need to get the decoded value.)
  *
  * Note: we expect the literal to be the most recently lexed token.  This
@@ -557,14 +557,14 @@ plpgsql_get_string_value(void)
 		/* Token is a $foo$...$foo$ string */
 		len = yyleng - 2 * dolqlen;
 		Assert(len >= 0);
-		result = (char *) malloc(len + 1);
+		result = (char *) palloc(len + 1);
 		memcpy(result, yytext + dolqlen, len);
 		result[len] = '\0';
 	}
 	else
 	{
 		/* Token is a '...' string */
-		result = (char *) malloc(yyleng + 1);	/* more than enough room */
+		result = (char *) palloc(yyleng + 1);	/* more than enough room */
 		len = 0;
 		for (cp = yytext; *cp; cp++)
 		{
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index a407c670a8764e012d7b0213ceffee5eb717b327..7fec95a2794660c362e7ee0cf8ca0391f2f80e3f 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -2173,3 +2173,72 @@ select refcursor_test2(20000) as "Should be false",
  f               | t
 (1 row)
 
+--
+-- tests for "raise" processing
+--
+create function raise_test1(int) returns int as $$
+begin
+    raise notice 'This message has too many parameters!', $1;
+    return $1;
+end;
+$$ language plpgsql;
+select raise_test1(5);
+ERROR:  too many parameters specified for RAISE
+CONTEXT:  PL/pgSQL function "raise_test1" line 2 at raise
+create function raise_test2(int) returns int as $$
+begin
+    raise notice 'This message has too few parameters: %, %, %', $1, $1;
+    return $1;
+end;
+$$ language plpgsql;
+select raise_test2(10);
+ERROR:  too few parameters specified for RAISE
+CONTEXT:  PL/pgSQL function "raise_test2" line 2 at raise
+--
+-- reject function definitions that contain malformed SQL queries at
+-- compile-time, where possible
+--
+create function bad_sql1() returns int as $$
+declare a int;
+begin
+    a := 5;
+    Johnny Yuma;
+    a := 10;
+    return a;
+end$$ language plpgsql;
+ERROR:  syntax error at or near "Johnny" at character 1
+QUERY:  Johnny Yuma
+CONTEXT:  SQL statement in PL/PgSQL function "bad_sql1" near line 4
+LINE 1: Johnny Yuma
+        ^
+create function bad_sql2() returns int as $$
+declare r record;
+begin
+    for r in select I fought the law, the law won LOOP
+        raise notice 'in loop';
+    end loop;
+    return 5;
+end;$$ language plpgsql;
+ERROR:  syntax error at or near "fought" at character 11
+QUERY:   select I fought the law, the law won
+CONTEXT:  SQL statement in PL/PgSQL function "bad_sql2" near line 3
+LINE 1:  select I fought the law, the law won
+                  ^
+-- a RETURN expression is mandatory, except for void-returning
+-- functions, where it is not allowed
+create function missing_return_expr() returns int as $$
+begin
+    return ;
+end;$$ language plpgsql;
+ERROR:  syntax error at end of input at character 8
+QUERY:  SELECT 
+CONTEXT:  SQL statement in PL/PgSQL function "missing_return_expr" near line 2
+LINE 1: SELECT 
+               ^
+create function void_return_expr() returns void as $$
+begin
+    return 5;
+end;$$ language plpgsql;
+ERROR:  function returning void cannot specify RETURN expression at or near "5" at character 72
+LINE 3:     return 5;
+                   ^
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 93827d143253e48279b6cb9185c832b1bb0f5bf7..607b7f288601015ec6607ab6b718ec7ce61c3868 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -1863,3 +1863,58 @@ $$ language 'plpgsql';
 
 select refcursor_test2(20000) as "Should be false",
        refcursor_test2(20) as "Should be true";
+
+--
+-- tests for "raise" processing
+--
+create function raise_test1(int) returns int as $$
+begin
+    raise notice 'This message has too many parameters!', $1;
+    return $1;
+end;
+$$ language plpgsql;
+
+select raise_test1(5);
+
+create function raise_test2(int) returns int as $$
+begin
+    raise notice 'This message has too few parameters: %, %, %', $1, $1;
+    return $1;
+end;
+$$ language plpgsql;
+
+select raise_test2(10);
+
+--
+-- reject function definitions that contain malformed SQL queries at
+-- compile-time, where possible
+--
+create function bad_sql1() returns int as $$
+declare a int;
+begin
+    a := 5;
+    Johnny Yuma;
+    a := 10;
+    return a;
+end$$ language plpgsql;
+
+create function bad_sql2() returns int as $$
+declare r record;
+begin
+    for r in select I fought the law, the law won LOOP
+        raise notice 'in loop';
+    end loop;
+    return 5;
+end;$$ language plpgsql;
+
+-- a RETURN expression is mandatory, except for void-returning
+-- functions, where it is not allowed
+create function missing_return_expr() returns int as $$
+begin
+    return ;
+end;$$ language plpgsql;
+
+create function void_return_expr() returns void as $$
+begin
+    return 5;
+end;$$ language plpgsql;
\ No newline at end of file