diff --git a/src/bin/scripts/createlang.c b/src/bin/scripts/createlang.c
index d81f42075590b1ff2d9b49b3b6cfc505414bbdad..64e257bc2f05d66aeff9b71c42e7c4568e16c07a 100644
--- a/src/bin/scripts/createlang.c
+++ b/src/bin/scripts/createlang.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/scripts/createlang.c,v 1.7 2003/11/29 19:52:07 pgsql Exp $
+ * $PostgreSQL: pgsql/src/bin/scripts/createlang.c,v 1.8 2004/03/19 18:58:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,8 +49,10 @@ main(int argc, char *argv[])
 
 	char	   *p;
 	bool		handlerexists;
+	bool		validatorexists;
 	bool		trusted;
 	char	   *handler;
+	char	   *validator = NULL;
 	char	   *object;
 
 	PQExpBufferData sql;
@@ -169,6 +171,7 @@ main(int argc, char *argv[])
 	{
 		trusted = true;
 		handler = "plpgsql_call_handler";
+		validator = "plpgsql_validator";
 		object = "plpgsql";
 	}
 	else if (strcmp(langname, "pltcl") == 0)
@@ -229,13 +232,26 @@ main(int argc, char *argv[])
 	/*
 	 * Check whether the call handler exists
 	 */
-	printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' AND prorettype = (SELECT oid FROM pg_type WHERE typname = 'language_handler') AND pronargs = 0;", handler);
+	printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' AND prorettype = 'pg_catalog.language_handler'::regtype AND pronargs = 0;", handler);
 	result = executeQuery(conn, sql.data, progname, echo);
 	handlerexists = (PQntuples(result) > 0);
 	PQclear(result);
 
 	/*
-	 * Create the call handler and the language
+	 * Check whether the validator exists
+	 */
+	if (validator)
+	{
+		printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' AND proargtypes[0] = 'pg_catalog.oid'::regtype AND pronargs = 1;", validator);
+		result = executeQuery(conn, sql.data, progname, echo);
+		validatorexists = (PQntuples(result) > 0);
+		PQclear(result);
+	}
+	else
+		validatorexists = true;			/* don't try to create it */
+
+	/*
+	 * Create the function(s) and the language
 	 */
 	resetPQExpBuffer(&sql);
 
@@ -244,10 +260,20 @@ main(int argc, char *argv[])
 						  "CREATE FUNCTION \"%s\" () RETURNS language_handler AS '%s/%s' LANGUAGE C;\n",
 						  handler, pglib, object);
 
+	if (!validatorexists)
+		appendPQExpBuffer(&sql,
+						  "CREATE FUNCTION \"%s\" (oid) RETURNS void AS '%s/%s' LANGUAGE C;\n",
+						  validator, pglib, object);
+
 	appendPQExpBuffer(&sql,
-					  "CREATE %sLANGUAGE \"%s\" HANDLER \"%s\";\n",
+					  "CREATE %sLANGUAGE \"%s\" HANDLER \"%s\"",
 					  (trusted ? "TRUSTED " : ""), langname, handler);
 
+	if (validator)
+		appendPQExpBuffer(&sql, " VALIDATOR \"%s\"", validator);
+
+	appendPQExpBuffer(&sql, ";\n");
+
 	if (echo)
 		printf("%s", sql.data);
 	result = PQexec(conn, sql.data);
diff --git a/src/bin/scripts/droplang.c b/src/bin/scripts/droplang.c
index 93c6816199507558bfdf2821da147efd3c2967fc..43f57115f53d47c781f759bcfc35f10f99679510 100644
--- a/src/bin/scripts/droplang.c
+++ b/src/bin/scripts/droplang.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/scripts/droplang.c,v 1.6 2003/11/29 19:52:07 pgsql Exp $
+ * $PostgreSQL: pgsql/src/bin/scripts/droplang.c,v 1.7 2004/03/19 18:58:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -14,6 +14,8 @@
 #include "common.h"
 #include "print.h"
 
+#define atooid(x)  ((Oid) strtoul((x), NULL, 10))
+
 
 static void help(const char *progname);
 
@@ -46,9 +48,12 @@ main(int argc, char *argv[])
 	char	   *langname = NULL;
 
 	char	   *p;
-	char	   *lanplcallfoid;
+	Oid			lanplcallfoid;
+	Oid			lanvalidator;
 	char	   *handler;
+	char	   *validator;
 	bool		keephandler;
+	bool		keepvalidator;
 
 	PQExpBufferData sql;
 
@@ -159,10 +164,10 @@ main(int argc, char *argv[])
 	conn = connectDatabase(dbname, host, port, username, password, progname);
 
 	/*
-	 * Make sure the language is installed and find the OID of the handler
-	 * function
+	 * Make sure the language is installed and find the OIDs of the handler
+	 * and validator functions
 	 */
-	printfPQExpBuffer(&sql, "SELECT lanplcallfoid FROM pg_language WHERE lanname = '%s' AND lanispl;", langname);
+	printfPQExpBuffer(&sql, "SELECT lanplcallfoid, lanvalidator FROM pg_language WHERE lanname = '%s' AND lanispl;", langname);
 	result = executeQuery(conn, sql.data, progname, echo);
 	if (PQntuples(result) == 0)
 	{
@@ -171,8 +176,9 @@ main(int argc, char *argv[])
 				progname, langname, dbname);
 		exit(1);
 	}
-	lanplcallfoid = PQgetvalue(result, 0, 0);
-	/* result not cleared! */
+	lanplcallfoid = atooid(PQgetvalue(result, 0, 0));
+	lanvalidator = atooid(PQgetvalue(result, 0, 1));
+	PQclear(result);
 
 	/*
 	 * Check that there are no functions left defined in that language
@@ -192,7 +198,7 @@ main(int argc, char *argv[])
 	/*
 	 * Check that the handler function isn't used by some other language
 	 */
-	printfPQExpBuffer(&sql, "SELECT count(*) FROM pg_language WHERE lanplcallfoid = %s AND lanname <> '%s';", lanplcallfoid, langname);
+	printfPQExpBuffer(&sql, "SELECT count(*) FROM pg_language WHERE lanplcallfoid = %u AND lanname <> '%s';", lanplcallfoid, langname);
 	result = executeQuery(conn, sql.data, progname, echo);
 	if (strcmp(PQgetvalue(result, 0, 0), "0") == 0)
 		keephandler = false;
@@ -205,20 +211,51 @@ main(int argc, char *argv[])
 	 */
 	if (!keephandler)
 	{
-		printfPQExpBuffer(&sql, "SELECT proname FROM pg_proc WHERE oid = %s;", lanplcallfoid);
+		printfPQExpBuffer(&sql, "SELECT proname FROM pg_proc WHERE oid = %u;", lanplcallfoid);
 		result = executeQuery(conn, sql.data, progname, echo);
-		handler = PQgetvalue(result, 0, 0);
-		/* result not cleared! */
+		handler = strdup(PQgetvalue(result, 0, 0));
+		PQclear(result);
 	}
 	else
 		handler = NULL;
 
 	/*
-	 * Drop the language
+	 * Check that the validator function isn't used by some other language
+	 */
+	if (OidIsValid(lanvalidator))
+	{
+		printfPQExpBuffer(&sql, "SELECT count(*) FROM pg_language WHERE lanvalidator = %u AND lanname <> '%s';", lanvalidator, langname);
+		result = executeQuery(conn, sql.data, progname, echo);
+		if (strcmp(PQgetvalue(result, 0, 0), "0") == 0)
+			keepvalidator = false;
+		else
+			keepvalidator = true;
+		PQclear(result);
+	}
+	else
+		keepvalidator = true;	/* don't try to delete it */
+
+	/*
+	 * Find the validator name
+	 */
+	if (!keepvalidator)
+	{
+		printfPQExpBuffer(&sql, "SELECT proname FROM pg_proc WHERE oid = %u;", lanvalidator);
+		result = executeQuery(conn, sql.data, progname, echo);
+		validator = strdup(PQgetvalue(result, 0, 0));
+		PQclear(result);
+	}
+	else
+		validator = NULL;
+
+	/*
+	 * Drop the language and the functions
 	 */
 	printfPQExpBuffer(&sql, "DROP LANGUAGE \"%s\";\n", langname);
 	if (!keephandler)
 		appendPQExpBuffer(&sql, "DROP FUNCTION \"%s\" ();\n", handler);
+	if (!keepvalidator)
+		appendPQExpBuffer(&sql, "DROP FUNCTION \"%s\" (oid);\n", validator);
 	if (echo)
 		printf("%s", sql.data);
 	result = PQexec(conn, sql.data);
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 8f18d50cefc7de68545af718b0298b811bfb59bd..8cbc0aa8e67a5ddb1a00e6d645ddb678d684c253 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.150 2004/02/24 22:59:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.151 2004/03/19 18:58:07 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -402,6 +402,7 @@ DATA(insert OID = 1003 (  _name		 PGNSP PGUID -1 f b t \054 0	19 array_in array_
 DATA(insert OID = 1005 (  _int2		 PGNSP PGUID -1 f b t \054 0	21 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1006 (  _int2vector PGNSP PGUID -1 f b t \054 0	22 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1007 (  _int4		 PGNSP PGUID -1 f b t \054 0	23 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+#define INT4ARRAYOID		1007
 DATA(insert OID = 1008 (  _regproc	 PGNSP PGUID -1 f b t \054 0	24 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1009 (  _text		 PGNSP PGUID -1 f b t \054 0	25 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1028 (  _oid		 PGNSP PGUID -1 f b t \054 0	26 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index a00d5586246d495839a43740758e8b7c6ec4c7ef..46e9ca925b72eaaba30fa85b5c37c475e8e2cac9 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.73 2004/01/07 18:56:30 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.74 2004/03/19 18:58:07 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -101,13 +101,15 @@ typedef struct plpgsql_hashent
  */
 static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
 		   HeapTuple procTup,
-		   PLpgSQL_func_hashkey *hashkey);
+		   PLpgSQL_func_hashkey *hashkey,
+		   bool forValidator);
 static void plpgsql_compile_error_callback(void *arg);
 static char **fetchArgNames(HeapTuple procTup, int nargs);
 static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
 static void compute_function_hashkey(FunctionCallInfo fcinfo,
 						 Form_pg_proc procStruct,
-						 PLpgSQL_func_hashkey *hashkey);
+						 PLpgSQL_func_hashkey *hashkey,
+						 bool forValidator);
 static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key);
 static void plpgsql_HashTableInsert(PLpgSQL_function *function,
 						PLpgSQL_func_hashkey *func_key);
@@ -134,12 +136,15 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
 /* ----------
  * plpgsql_compile		Make an execution tree for a PL/pgSQL function.
  *
+ * If forValidator is true, we're only compiling for validation purposes,
+ * and so some checks are skipped.
+ *
  * Note: it's important for this to fall through quickly if the function
  * has already been compiled.
  * ----------
  */
 PLpgSQL_function *
-plpgsql_compile(FunctionCallInfo fcinfo)
+plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
 {
 	Oid			funcOid = fcinfo->flinfo->fn_oid;
 	HeapTuple	procTup;
@@ -171,7 +176,7 @@ plpgsql_compile(FunctionCallInfo fcinfo)
 			plpgsql_HashTableInit();
 
 		/* Compute hashkey using function signature and actual arg types */
-		compute_function_hashkey(fcinfo, procStruct, &hashkey);
+		compute_function_hashkey(fcinfo, procStruct, &hashkey, forValidator);
 		hashkey_valid = true;
 
 		/* And do the lookup */
@@ -205,12 +210,13 @@ plpgsql_compile(FunctionCallInfo fcinfo)
 		 * the completed function.
 		 */
 		if (!hashkey_valid)
-			compute_function_hashkey(fcinfo, procStruct, &hashkey);
+			compute_function_hashkey(fcinfo, procStruct, &hashkey,
+									 forValidator);
 
 		/*
 		 * Do the hard part.
 		 */
-		function = do_compile(fcinfo, procTup, &hashkey);
+		function = do_compile(fcinfo, procTup, &hashkey, forValidator);
 	}
 
 	ReleaseSysCache(procTup);
@@ -232,7 +238,8 @@ plpgsql_compile(FunctionCallInfo fcinfo)
 static PLpgSQL_function *
 do_compile(FunctionCallInfo fcinfo,
 		   HeapTuple procTup,
-		   PLpgSQL_func_hashkey *hashkey)
+		   PLpgSQL_func_hashkey *hashkey,
+		   bool forValidator)
 {
 	Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
 	int			functype = CALLED_AS_TRIGGER(fcinfo) ? T_TRIGGER : T_FUNCTION;
@@ -308,7 +315,8 @@ do_compile(FunctionCallInfo fcinfo,
 			/*
 			 * Check for a polymorphic returntype. If found, use the
 			 * actual returntype type from the caller's FuncExpr node, if
-			 * we have one.
+			 * we have one.  (In validation mode we arbitrarily assume we
+			 * are dealing with integers.)
 			 *
 			 * Note: errcode is FEATURE_NOT_SUPPORTED because it should
 			 * always work; if it doesn't we're in some context that fails
@@ -317,7 +325,15 @@ do_compile(FunctionCallInfo fcinfo,
 			rettypeid = procStruct->prorettype;
 			if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID)
 			{
-				rettypeid = get_fn_expr_rettype(fcinfo->flinfo);
+				if (forValidator)
+				{
+					if (rettypeid == ANYARRAYOID)
+						rettypeid = INT4ARRAYOID;
+					else
+						rettypeid = INT4OID;
+				}
+				else
+					rettypeid = get_fn_expr_rettype(fcinfo->flinfo);
 				if (!OidIsValid(rettypeid))
 					ereport(ERROR,
 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -1758,22 +1774,6 @@ plpgsql_add_initdatums(int **varnos)
 }
 
 
-/* ---------
- * plpgsql_yyerror			Handle parser error
- * ---------
- */
-
-void
-plpgsql_yyerror(const char *s)
-{
-	plpgsql_error_lineno = plpgsql_scanner_lineno();
-	ereport(ERROR,
-			(errcode(ERRCODE_SYNTAX_ERROR),
-	/* translator: first %s is a phrase like "syntax error" */
-			 errmsg("%s at or near \"%s\"", s, plpgsql_yytext)));
-}
-
-
 /*
  * Compute the hashkey for a given function invocation
  *
@@ -1782,7 +1782,8 @@ plpgsql_yyerror(const char *s)
 static void
 compute_function_hashkey(FunctionCallInfo fcinfo,
 						 Form_pg_proc procStruct,
-						 PLpgSQL_func_hashkey *hashkey)
+						 PLpgSQL_func_hashkey *hashkey,
+						 bool forValidator)
 {
 	int			i;
 
@@ -1792,8 +1793,12 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
 	/* get function OID */
 	hashkey->funcOid = fcinfo->flinfo->fn_oid;
 
-	/* if trigger, get relation OID */
-	if (CALLED_AS_TRIGGER(fcinfo))
+	/*
+	 * if trigger, get relation OID.  In validation mode we do not know what
+	 * relation is intended to be used, so we leave trigrelOid zero; the
+	 * hash entry built in this case will never really be used.
+	 */
+	if (CALLED_AS_TRIGGER(fcinfo) && !forValidator)
 	{
 		TriggerData *trigdata = (TriggerData *) fcinfo->context;
 
@@ -1808,6 +1813,9 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
 		/*
 		 * Check for polymorphic arguments. If found, use the actual
 		 * parameter type from the caller's FuncExpr node, if we have one.
+		 * (In validation mode we arbitrarily assume we are dealing with
+		 * integers.  This lets us build a valid, if possibly useless,
+		 * function hashtable entry.)
 		 *
 		 * We can support arguments of type ANY the same way as normal
 		 * polymorphic arguments.
@@ -1815,7 +1823,15 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
 		if (argtypeid == ANYARRAYOID || argtypeid == ANYELEMENTOID ||
 			argtypeid == ANYOID)
 		{
-			argtypeid = get_fn_expr_argtype(fcinfo->flinfo, i);
+			if (forValidator)
+			{
+				if (argtypeid == ANYARRAYOID)
+					argtypeid = INT4ARRAYOID;
+				else
+					argtypeid = INT4OID;
+			}
+			else
+				argtypeid = get_fn_expr_argtype(fcinfo->flinfo, i);
 			if (!OidIsValid(argtypeid))
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 3f9998f490008f7b2c4ea9526471895ba45e2cfa..5feeb55cce75dd84474d426ac81ed3523659e87b 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.19 2003/11/29 19:52:12 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.20 2004/03/19 18:58:07 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -42,8 +42,11 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
+extern bool check_function_bodies;
+
 static int	plpgsql_firstcall = 1;
 
 static void plpgsql_init_all(void);
@@ -88,7 +91,6 @@ plpgsql_init_all(void)
 /* ----------
  * plpgsql_call_handler
  *
- * This is the only visible function of the PL interpreter.
  * The PostgreSQL function manager and trigger manager
  * call this function for execution of PL/pgSQL procedures.
  * ----------
@@ -111,7 +113,7 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
 		elog(ERROR, "SPI_connect failed");
 
 	/* Find or compile the function */
-	func = plpgsql_compile(fcinfo);
+	func = plpgsql_compile(fcinfo, false);
 
 	/*
 	 * Determine if called as function or trigger and call appropriate
@@ -131,3 +133,118 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
 
 	return retval;
 }
+
+/* ----------
+ * plpgsql_validator
+ *
+ * This function attempts to validate a PL/pgSQL function at
+ * CREATE FUNCTION time.
+ * ----------
+ */
+PG_FUNCTION_INFO_V1(plpgsql_validator);
+
+Datum
+plpgsql_validator(PG_FUNCTION_ARGS)
+{
+	Oid			funcoid = PG_GETARG_OID(0);
+	HeapTuple	tuple;
+	Form_pg_proc proc;
+	char		functyptype;
+	bool		istrigger = false;
+	bool		haspolyresult;
+	bool		haspolyarg;
+	int			i;
+
+	/* perform initialization */
+	plpgsql_init_all();
+
+	/* Get the new function's pg_proc entry */
+	tuple = SearchSysCache(PROCOID,
+						   ObjectIdGetDatum(funcoid),
+						   0, 0, 0);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for function %u", funcoid);
+	proc = (Form_pg_proc) GETSTRUCT(tuple);
+
+	functyptype = get_typtype(proc->prorettype);
+
+	/* Disallow pseudotype result */
+	/* except for TRIGGER, RECORD, VOID, ANYARRAY, or ANYELEMENT */
+	if (functyptype == 'p')
+	{
+		/* we assume OPAQUE with no arguments means a trigger */
+		if (proc->prorettype == TRIGGEROID ||
+			(proc->prorettype == OPAQUEOID && proc->pronargs == 0))
+			istrigger = true;
+		else if (proc->prorettype == ANYARRAYOID ||
+				 proc->prorettype == ANYELEMENTOID)
+			haspolyresult = true;
+		else if (proc->prorettype != RECORDOID &&
+				 proc->prorettype != VOIDOID)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("plpgsql functions cannot return type %s",
+							format_type_be(proc->prorettype))));
+	}
+
+	/* Disallow pseudotypes in arguments */
+	/* except for ANYARRAY or ANYELEMENT */
+	haspolyarg = false;
+	for (i = 0; i < proc->pronargs; i++)
+	{
+		if (get_typtype(proc->proargtypes[i]) == 'p')
+		{
+			if (proc->proargtypes[i] == ANYARRAYOID ||
+				proc->proargtypes[i] == ANYELEMENTOID)
+				haspolyarg = true;
+			else
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("plpgsql functions cannot take type %s",
+								format_type_be(proc->proargtypes[i]))));
+		}
+	}
+
+	/* Postpone body checks if !check_function_bodies */
+	if (check_function_bodies)
+	{
+		FunctionCallInfoData fake_fcinfo;
+		FmgrInfo	flinfo;
+		TriggerData trigdata;
+
+		/*
+		 * Connect to SPI manager (is this needed for compilation?)
+		 */
+		if (SPI_connect() != SPI_OK_CONNECT)
+			elog(ERROR, "SPI_connect failed");
+
+		/*
+		 * Set up a fake fcinfo with just enough info to satisfy
+		 * plpgsql_compile().
+		 */
+		MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+		MemSet(&flinfo, 0, sizeof(flinfo));
+		fake_fcinfo.flinfo = &flinfo;
+		flinfo.fn_oid = funcoid;
+		flinfo.fn_mcxt = CurrentMemoryContext;
+		if (istrigger)
+		{
+			MemSet(&trigdata, 0, sizeof(trigdata));
+			trigdata.type = T_TriggerData;
+			fake_fcinfo.context = (Node *) &trigdata;
+		}
+
+		/* Test-compile the function */
+		plpgsql_compile(&fake_fcinfo, true);
+
+		/*
+		 * Disconnect from SPI manager
+		 */
+		if (SPI_finish() != SPI_OK_FINISH)
+			elog(ERROR, "SPI_finish failed");
+	}
+
+	ReleaseSysCache(tuple);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 911e331adf2175c9538f061ae277a04b752613f3..cb3c4c2944feb013785fa1b72079e8fb2b4757f6 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.44 2004/02/25 18:10:51 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.45 2004/03/19 18:58:07 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -620,7 +620,8 @@ extern PLpgSQL_function *plpgsql_curr_compile;
  * Functions in pl_comp.c
  * ----------
  */
-extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo);
+extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
+										 bool forValidator);
 extern int	plpgsql_parse_word(char *word);
 extern int	plpgsql_parse_dblword(char *word);
 extern int	plpgsql_parse_tripword(char *word);
@@ -633,7 +634,6 @@ extern PLpgSQL_type *plpgsql_parse_datatype(char *string);
 extern PLpgSQL_row *plpgsql_build_rowtype(Oid classOid);
 extern void plpgsql_adddatum(PLpgSQL_datum * new);
 extern int	plpgsql_add_initdatums(int **varnos);
-extern void plpgsql_yyerror(const char *s);
 extern void plpgsql_HashTableInit(void);
 
 /* ----------
@@ -642,6 +642,7 @@ extern void plpgsql_HashTableInit(void);
  */
 extern void plpgsql_init(void);
 extern Datum plpgsql_call_handler(PG_FUNCTION_ARGS);
+extern Datum plpgsql_validator(PG_FUNCTION_ARGS);
 
 /* ----------
  * Functions in pl_exec.c
@@ -691,6 +692,7 @@ extern int	plpgsql_yyparse(void);
 extern int	plpgsql_base_yylex(void);
 extern int	plpgsql_yylex(void);
 extern void plpgsql_push_back_token(int token);
+extern void plpgsql_yyerror(const char *message);
 extern int	plpgsql_scanner_lineno(void);
 extern void plpgsql_scanner_init(const char *str, int functype);
 extern void plpgsql_scanner_finish(void);
diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l
index de447e09f1b711f89a0b7ae26f525a69784ad4cd..3e6ee8fd187be273d1b497f7f130645cf617b3d5 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.32 2004/02/25 18:10:51 tgl Exp $
+ *    $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.33 2004/03/19 18:58:07 tgl Exp $
  *
  *    This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -38,6 +38,8 @@
 
 #include "plpgsql.h"
 
+#include "mb/pg_wchar.h"
+
 
 /* No reason to constrain amount of data slurped */
 #define YY_READ_BUF_SIZE 16777216
@@ -409,6 +411,38 @@ plpgsql_push_back_token(int token)
 	have_pushback_token = true;
 }
 
+/*
+ * Report a syntax error.
+ */
+void
+plpgsql_yyerror(const char *message)
+{
+	const char *loc = yytext;
+	int			cursorpos;
+
+	plpgsql_error_lineno = plpgsql_scanner_lineno();
+
+	/* in multibyte encodings, return index in characters not bytes */
+	cursorpos = pg_mbstrlen_with_len(scanbuf, loc - scanbuf) + 1;
+
+	if (*loc == YY_END_OF_BUFFER_CHAR)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 /* translator: %s is typically "syntax error" */
+				 errmsg("%s at end of input", message),
+				 errposition(cursorpos)));
+	}
+	else
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 /* translator: first %s is typically "syntax error" */
+				 errmsg("%s at or near \"%s\"", message, loc),
+				 errposition(cursorpos)));
+	}
+}
+
 /*
  * Get the line number at which the current token ends.  This substitutes
  * for flex's very poorly implemented yylineno facility.
@@ -439,19 +473,6 @@ plpgsql_scanner_init(const char *str, int functype)
 {
 	Size	slen;
 
-	/*----------
-	 * Hack: skip any initial newline, so that in the common coding layout
-	 *		CREATE FUNCTION ... AS '
-	 *			code body
-	 *		' LANGUAGE 'plpgsql';
-	 * we will think "line 1" is what the programmer thinks of as line 1.
-	 *----------
-	 */
-    if (*str == '\r')
-        str++;
-    if (*str == '\n')
-        str++;
-
 	slen = strlen(str);
 
 	/*
@@ -478,6 +499,19 @@ plpgsql_scanner_init(const char *str, int functype)
 	cur_line_start = scanbuf;
 	cur_line_num = 1;
 
+	/*----------
+	 * Hack: skip any initial newline, so that in the common coding layout
+	 *		CREATE FUNCTION ... AS '
+	 *			code body
+	 *		' LANGUAGE 'plpgsql';
+	 * we will think "line 1" is what the programmer thinks of as line 1.
+	 *----------
+	 */
+    if (*cur_line_start == '\r')
+        cur_line_start++;
+    if (*cur_line_start == '\n')
+        cur_line_start++;
+
 	BEGIN(INITIAL);
 }