diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 5d8e945a66ad19c20febb800d142d4a6897fdd81..c65f04da402cca9e5c7d4116f71f06ed747c00d9 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -4,7 +4,7 @@
 #    Makefile for commands
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.4 1997/08/31 11:40:12 vadim Exp $
+#    $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.5 1997/10/28 14:54:43 vadim Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -19,7 +19,7 @@ CFLAGS+=$(INCLUDE_OPT)
 
 OBJS = async.o creatinh.o command.o copy.o defind.o define.o \
        purge.o remove.o rename.o vacuum.o version.o view.o cluster.o \
-       recipe.o explain.o sequence.o trigger.o
+       recipe.o explain.o sequence.o trigger.o proclang.o
 
 all: SUBSYS.o
 
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index 903bb5164953f71bc757fef085b4265c1867767b..9aa8c09897329c5e3fa27017fecb9b4040f80429 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.16 1997/09/08 21:42:38 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.17 1997/10/28 14:54:46 vadim Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -45,6 +45,7 @@
 #include <catalog/pg_operator.h>
 #include <catalog/pg_proc.h>
 #include <catalog/pg_type.h>
+#include <catalog/pg_language.h>
 #include <utils/syscache.h>
 #include <fmgr.h>				/* for fmgr */
 #include <utils/builtins.h>		/* prototype for textin() */
@@ -239,6 +240,8 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
 	bool		canCache;
 	bool		returnsSet;
 
+	bool		lanisPL = false;
+
 	/* The function returns a set of values, as opposed to a singleton. */
 
 
@@ -262,19 +265,59 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
 	}
 	else
 	{
-		elog(WARN,
+		HeapTuple		languageTuple;
+		Form_pg_language	languageStruct;
+
+	        /* Lookup the language in the system cache */
+		languageTuple = SearchSysCacheTuple(LANNAME,
+			PointerGetDatum(languageName),
+			0, 0, 0);
+		
+		if (!HeapTupleIsValid(languageTuple)) {
+
+		    elog(WARN,
 			 "Unrecognized language specified in a CREATE FUNCTION: "
-			 "'%s'.  Recognized languages are sql, C, and internal.",
+			 "'%s'.  Recognized languages are sql, C, internal "
+			 "and the created procedural languages.",
 			 languageName);
+		}
+
+		/* Check that this language is a PL */
+		languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
+		if (!(languageStruct->lanispl)) {
+		    elog(WARN,
+		    	"Language '%s' isn't defined as PL", languageName);
+		}
+
+		/*
+		 * Functions in untrusted procedural languages are
+		 * restricted to be defined by postgres superusers only
+		 */
+		if (languageStruct->lanpltrusted == false && !superuser()) {
+		    elog(WARN, "Only users with Postgres superuser privilege "
+		    	"are permitted to create a function in the '%s' "
+			"language.",
+			languageName);
+		}
+
+		lanisPL = true;
+
+		/*
+		 * These are meaningless
+		 */
+		perbyte_cpu = percall_cpu = 0;
+		byte_pct = outin_ratio = 100;
+		canCache = false;
 	}
 
 	interpret_AS_clause(languageName, stmt->as, &prosrc_str, &probin_str);
 
-	if (strcmp(languageName, "sql") != 0 && !superuser())
+	if (strcmp(languageName, "sql") != 0 && lanisPL == false && !superuser())
 		elog(WARN,
 			 "Only users with Postgres superuser privilege are permitted "
 			 "to create a function "
-			 "in the '%s' language.  Others may use the 'sql' language.",
+			 "in the '%s' language.  Others may use the 'sql' language "
+			 "or the created procedural languages.",
 			 languageName);
 	/* Above does not return. */
 	else
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
new file mode 100644
index 0000000000000000000000000000000000000000..2b8836b70bbf8cdd654938b89f36008741c35287
--- /dev/null
+++ b/src/backend/commands/proclang.c
@@ -0,0 +1,205 @@
+/*-------------------------------------------------------------------------
+ *
+ * proclang.c--
+ *	  PostgreSQL PROCEDURAL LANGUAGE support code.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/catname.h"
+#include "catalog/pg_user.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_language.h"
+#include "utils/syscache.h"
+#include "commands/proclang.h"
+#include "fmgr.h"
+
+
+static void
+case_translate_language_name(const char *input, char *output)
+{
+/*-------------------------------------------------------------------------
+  Translate the input language name to lower case, except if it's C,
+  translate to upper case.
+--------------------------------------------------------------------------*/
+	int			i;
+
+	for (i = 0; i < NAMEDATALEN && input[i] != '\0'; ++i)
+		output[i] = tolower(input[i]);
+
+	output[i] = '\0';
+
+	if (strcmp(output, "c") == 0)
+		output[0] = 'C';
+}
+
+
+/* ---------------------------------------------------------------------
+ * CREATE PROCEDURAL LANGUAGE
+ * ---------------------------------------------------------------------
+ */
+void
+CreateProceduralLanguage(CreatePLangStmt * stmt)
+{
+	char		languageName[NAMEDATALEN];
+	HeapTuple	langTup;
+	HeapTuple	procTup;
+
+	Oid			typev[8];
+	char		nulls[Natts_pg_language];
+	Datum		values[Natts_pg_language];
+	Relation	rdesc;
+	HeapTuple	tup;
+	TupleDesc	tupDesc;
+
+	int			i;
+
+	/* ----------------
+	 * Check permission
+	 * ----------------
+	 */
+	if (!superuser())
+	{
+		elog(WARN, "Only users with Postgres superuser privilege are "
+			 "permitted to create procedural languages");
+	}
+
+	/* ----------------
+	 * Translate the language name and check that
+	 * this language doesn't already exist
+	 * ----------------
+	 */
+	case_translate_language_name(stmt->plname, languageName);
+
+	langTup = SearchSysCacheTuple(LANNAME,
+								  PointerGetDatum(languageName),
+								  0, 0, 0);
+	if (HeapTupleIsValid(langTup))
+	{
+		elog(WARN, "Language %s already exists", languageName);
+	}
+
+	/* ----------------
+	 * Lookup the PL handler function and check that it is
+	 * of return type Opaque
+	 * ----------------
+	 */
+	memset(typev, 0, sizeof(typev));
+	procTup = SearchSysCacheTuple(PRONAME,
+								  PointerGetDatum(stmt->plhandler),
+								  UInt16GetDatum(0),
+								  PointerGetDatum(typev),
+								  0);
+	if (!HeapTupleIsValid(procTup))
+	{
+		elog(WARN, "PL handler function %s() doesn't exist",
+			 stmt->plhandler);
+	}
+	if (((Form_pg_proc) GETSTRUCT(procTup))->prorettype != InvalidOid)
+	{
+		elog(WARN, "PL handler function %s() isn't of return type Opaque",
+			 stmt->plhandler);
+	}
+
+	/* ----------------
+	 * Insert the new language into pg_language
+	 * ----------------
+	 */
+	for (i = 0; i < Natts_pg_language; i++)
+	{
+		nulls[i] = ' ';
+		values[i] = (Datum) NULL;
+	}
+
+	i = 0;
+	values[i++] = PointerGetDatum(languageName);
+	values[i++] = Int8GetDatum((bool) 1);
+	values[i++] = Int8GetDatum(stmt->pltrusted);
+	values[i++] = ObjectIdGetDatum(procTup->t_oid);
+	values[i++] = (Datum) fmgr(TextInRegProcedure, stmt->plcompiler);
+
+	rdesc = heap_openr(LanguageRelationName);
+
+	tupDesc = rdesc->rd_att;
+	tup = heap_formtuple(tupDesc, values, nulls);
+
+	heap_insert(rdesc, tup);
+
+	heap_close(rdesc);
+	return;
+}
+
+
+/* ---------------------------------------------------------------------
+ * DROP PROCEDURAL LANGUAGE
+ * ---------------------------------------------------------------------
+ */
+void
+DropProceduralLanguage(DropPLangStmt * stmt)
+{
+	char		languageName[NAMEDATALEN];
+	HeapTuple	langTup;
+
+	Relation	rdesc;
+	HeapScanDesc scanDesc;
+	ScanKeyData scanKeyData;
+	HeapTuple	tup;
+
+	/* ----------------
+	 * Check permission
+	 * ----------------
+	 */
+	if (!superuser())
+	{
+		elog(WARN, "Only users with Postgres superuser privilege are "
+			 "permitted to drop procedural languages");
+	}
+
+	/* ----------------
+	 * Translate the language name, check that
+	 * this language exist and is a PL
+	 * ----------------
+	 */
+	case_translate_language_name(stmt->plname, languageName);
+
+	langTup = SearchSysCacheTuple(LANNAME,
+								  PointerGetDatum(languageName),
+								  0, 0, 0);
+	if (!HeapTupleIsValid(langTup))
+	{
+		elog(WARN, "Language %s doesn't exist", languageName);
+	}
+
+	if (!((Form_pg_language) GETSTRUCT(langTup))->lanispl)
+	{
+		elog(WARN, "Language %s isn't a created procedural language",
+			 languageName);
+	}
+
+	/* ----------------
+	 * Now scan pg_language and delete the PL tuple
+	 * ----------------
+	 */
+	rdesc = heap_openr(LanguageRelationName);
+
+	ScanKeyEntryInitialize(&scanKeyData, 0, Anum_pg_language_lanname,
+						   F_NAMEEQ, PointerGetDatum(languageName));
+
+	scanDesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, &scanKeyData);
+
+	tup = heap_getnext(scanDesc, 0, (Buffer *) NULL);
+
+	if (!HeapTupleIsValid(tup))
+	{
+		elog(WARN, "Language with name '%s' not found", languageName);
+	}
+
+	heap_delete(rdesc, &(tup->t_ctid));
+
+	heap_endscan(scanDesc);
+	heap_close(rdesc);
+}
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 9215f41055596fd433bc0bd2f6b3d124983e58cc..273136b29233811edc59c54b790ef261fd657c10 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -26,11 +26,11 @@
 #include "utils/mcxt.h"
 #include "utils/inval.h"
 #include "utils/builtins.h"
+#include "utils/syscache.h"
 
 #ifndef NO_SECURITY
 #include "miscadmin.h"
 #include "utils/acl.h"
-#include "utils/syscache.h"
 #endif
 
 TriggerData *CurrentTriggerData = NULL;
@@ -87,8 +87,8 @@ CreateTrigger(CreateTrigStmt * stmt)
 	if (stmt->row)
 		TRIGGER_SETT_ROW(tgtype);
 	else
-		elog (WARN, "CreateTrigger: STATEMENT triggers are unimplemented, yet");
-	
+		elog(WARN, "CreateTrigger: STATEMENT triggers are unimplemented, yet");
+
 	for (i = 0; i < 3 && stmt->actions[i]; i++)
 	{
 		switch (stmt->actions[i])
@@ -142,7 +142,22 @@ CreateTrigger(CreateTrigStmt * stmt)
 		elog(WARN, "CreateTrigger: function %s () does not exist", stmt->funcname);
 
 	if (((Form_pg_proc) GETSTRUCT(tuple))->prolang != ClanguageId)
-		elog(WARN, "CreateTrigger: only C functions are supported");
+	{
+		HeapTuple	langTup;
+
+		langTup = SearchSysCacheTuple(LANOID,
+			ObjectIdGetDatum(((Form_pg_proc) GETSTRUCT(tuple))->prolang),
+									  0, 0, 0);
+		if (!HeapTupleIsValid(langTup))
+		{
+			elog(WARN, "CreateTrigger: cache lookup for PL failed");
+		}
+
+		if (((Form_pg_language) GETSTRUCT(langTup))->lanispl == false)
+		{
+			elog(WARN, "CreateTrigger: only C and PL functions are supported");
+		}
+	}
 
 	MemSet(nulls, ' ', Natts_pg_trigger * sizeof(char));
 
@@ -159,10 +174,10 @@ CreateTrigger(CreateTrigStmt * stmt)
 
 		foreach(le, stmt->args)
 		{
-			char   *ar = (char *) lfirst(le);
+			char	   *ar = (char *) lfirst(le);
 
 			len += strlen(ar) + 4;
-			for ( ; *ar; ar++)
+			for (; *ar; ar++)
 			{
 				if (*ar == '\\')
 					len++;
@@ -172,9 +187,9 @@ CreateTrigger(CreateTrigStmt * stmt)
 		args[0] = 0;
 		foreach(le, stmt->args)
 		{
-			char   *s = (char *) lfirst(le);
-			char   *d = args + strlen(args);
-			
+			char	   *s = (char *) lfirst(le);
+			char	   *d = args + strlen(args);
+
 			while (*s)
 			{
 				if (*s == '\\')
@@ -399,6 +414,7 @@ RelationBuildTriggers(Relation relation)
 		build->tgname = nameout(&(pg_trigger->tgname));
 		build->tgfoid = pg_trigger->tgfoid;
 		build->tgfunc = NULL;
+		build->tgplfunc = NULL;
 		build->tgtype = pg_trigger->tgtype;
 		build->tgnargs = pg_trigger->tgnargs;
 		memcpy(build->tgattr, &(pg_trigger->tgattr), 8 * sizeof(int16));
@@ -578,6 +594,54 @@ DescribeTrigger(TriggerDesc * trigdesc, Trigger * trigger)
 
 }
 
+static HeapTuple
+ExecCallTriggerFunc(Trigger * trigger)
+{
+
+	if (trigger->tgfunc != NULL)
+	{
+		return (HeapTuple) ((*(trigger->tgfunc)) ());
+	}
+
+	if (trigger->tgplfunc == NULL)
+	{
+		HeapTuple	procTuple;
+		HeapTuple	langTuple;
+		Form_pg_proc procStruct;
+		Form_pg_language langStruct;
+		int			nargs;
+
+		procTuple = SearchSysCacheTuple(PROOID,
+										ObjectIdGetDatum(trigger->tgfoid),
+										0, 0, 0);
+		if (!HeapTupleIsValid(procTuple))
+		{
+			elog(WARN, "ExecCallTriggerFunc(): Cache lookup for proc %ld failed",
+				 ObjectIdGetDatum(trigger->tgfoid));
+		}
+		procStruct = (Form_pg_proc) GETSTRUCT(procTuple);
+
+		langTuple = SearchSysCacheTuple(LANOID,
+								   ObjectIdGetDatum(procStruct->prolang),
+										0, 0, 0);
+		if (!HeapTupleIsValid(langTuple))
+		{
+			elog(WARN, "ExecCallTriggerFunc(): Cache lookup for language %ld failed",
+				 ObjectIdGetDatum(procStruct->prolang));
+		}
+		langStruct = (Form_pg_language) GETSTRUCT(langTuple);
+
+		if (langStruct->lanispl == false)
+		{
+			fmgr_info(trigger->tgfoid, &(trigger->tgfunc), &nargs);
+			return (HeapTuple) ((*(trigger->tgfunc)) ());
+		}
+		fmgr_info(langStruct->lanplcallfoid, &(trigger->tgplfunc), &nargs);
+	}
+
+	return (HeapTuple) ((*(trigger->tgplfunc)) (trigger->tgfoid));
+}
+
 HeapTuple
 ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple)
 {
@@ -586,7 +650,6 @@ ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple)
 	Trigger   **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT];
 	HeapTuple	newtuple = trigtuple;
 	HeapTuple	oldtuple;
-	int			nargs;
 	int			i;
 
 	SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
@@ -599,9 +662,7 @@ ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple)
 		CurrentTriggerData = SaveTriggerData;
 		CurrentTriggerData->tg_trigtuple = oldtuple = newtuple;
 		CurrentTriggerData->tg_trigger = trigger[i];
-		if (trigger[i]->tgfunc == NULL)
-			fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
-		newtuple = (HeapTuple) ((*(trigger[i]->tgfunc)) ());
+		newtuple = ExecCallTriggerFunc(trigger[i]);
 		if (newtuple == NULL)
 			break;
 		else if (oldtuple != newtuple && oldtuple != trigtuple)
@@ -618,7 +679,6 @@ ExecARInsertTriggers(Relation rel, HeapTuple trigtuple)
 	TriggerData *SaveTriggerData;
 	int			ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT];
 	Trigger   **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_INSERT];
-	int			nargs;
 	int			i;
 
 	SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
@@ -630,9 +690,7 @@ ExecARInsertTriggers(Relation rel, HeapTuple trigtuple)
 		CurrentTriggerData = SaveTriggerData;
 		CurrentTriggerData->tg_trigtuple = trigtuple;
 		CurrentTriggerData->tg_trigger = trigger[i];
-		if (trigger[i]->tgfunc == NULL)
-			fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
-		(void) ((*(trigger[i]->tgfunc)) ());
+		ExecCallTriggerFunc(trigger[i]);
 	}
 	CurrentTriggerData = NULL;
 	pfree(SaveTriggerData);
@@ -647,7 +705,6 @@ ExecBRDeleteTriggers(Relation rel, ItemPointer tupleid)
 	Trigger   **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_DELETE];
 	HeapTuple	trigtuple;
 	HeapTuple	newtuple = NULL;
-	int			nargs;
 	int			i;
 
 	trigtuple = GetTupleForTrigger(rel, tupleid, true);
@@ -664,9 +721,7 @@ ExecBRDeleteTriggers(Relation rel, ItemPointer tupleid)
 		CurrentTriggerData = SaveTriggerData;
 		CurrentTriggerData->tg_trigtuple = trigtuple;
 		CurrentTriggerData->tg_trigger = trigger[i];
-		if (trigger[i]->tgfunc == NULL)
-			fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
-		newtuple = (HeapTuple) ((*(trigger[i]->tgfunc)) ());
+		newtuple = ExecCallTriggerFunc(trigger[i]);
 		if (newtuple == NULL)
 			break;
 	}
@@ -684,7 +739,6 @@ ExecARDeleteTriggers(Relation rel, ItemPointer tupleid)
 	int			ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE];
 	Trigger   **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_DELETE];
 	HeapTuple	trigtuple;
-	int			nargs;
 	int			i;
 
 	trigtuple = GetTupleForTrigger(rel, tupleid, false);
@@ -700,9 +754,7 @@ ExecARDeleteTriggers(Relation rel, ItemPointer tupleid)
 		CurrentTriggerData = SaveTriggerData;
 		CurrentTriggerData->tg_trigtuple = trigtuple;
 		CurrentTriggerData->tg_trigger = trigger[i];
-		if (trigger[i]->tgfunc == NULL)
-			fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
-		(void) ((*(trigger[i]->tgfunc)) ());
+		ExecCallTriggerFunc(trigger[i]);
 	}
 	CurrentTriggerData = NULL;
 	pfree(SaveTriggerData);
@@ -719,7 +771,6 @@ ExecBRUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple newtuple)
 	HeapTuple	trigtuple;
 	HeapTuple	oldtuple;
 	HeapTuple	intuple = newtuple;
-	int			nargs;
 	int			i;
 
 	trigtuple = GetTupleForTrigger(rel, tupleid, true);
@@ -736,9 +787,7 @@ ExecBRUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple newtuple)
 		CurrentTriggerData->tg_trigtuple = trigtuple;
 		CurrentTriggerData->tg_newtuple = oldtuple = newtuple;
 		CurrentTriggerData->tg_trigger = trigger[i];
-		if (trigger[i]->tgfunc == NULL)
-			fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
-		newtuple = (HeapTuple) ((*(trigger[i]->tgfunc)) ());
+		newtuple = ExecCallTriggerFunc(trigger[i]);
 		if (newtuple == NULL)
 			break;
 		else if (oldtuple != newtuple && oldtuple != intuple)
@@ -757,7 +806,6 @@ ExecARUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple newtuple)
 	int			ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE];
 	Trigger   **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_UPDATE];
 	HeapTuple	trigtuple;
-	int			nargs;
 	int			i;
 
 	trigtuple = GetTupleForTrigger(rel, tupleid, false);
@@ -773,9 +821,7 @@ ExecARUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple newtuple)
 		CurrentTriggerData->tg_trigtuple = trigtuple;
 		CurrentTriggerData->tg_newtuple = newtuple;
 		CurrentTriggerData->tg_trigger = trigger[i];
-		if (trigger[i]->tgfunc == NULL)
-			fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
-		(void) ((*(trigger[i]->tgfunc)) ());
+		ExecCallTriggerFunc(trigger[i]);
 	}
 	CurrentTriggerData = NULL;
 	pfree(SaveTriggerData);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8132cc34d4837d9ce32b46204a322615931821c6..7eb361d3a93e21541479b3785d9a1560980fd1f5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.58 1997/10/25 05:56:41 thomas Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.59 1997/10/28 14:56:08 vadim Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -109,6 +109,7 @@ static char *FlattenStringList(List *list);
 		AddAttrStmt, ClosePortalStmt,
 		CopyStmt, CreateStmt, CreateSeqStmt, DefineStmt, DestroyStmt,
 		ExtendStmt, FetchStmt,	GrantStmt, CreateTrigStmt, DropTrigStmt,
+		CreatePLangStmt, DropPLangStmt,
 		IndexStmt, ListenStmt, OptimizableStmt,
 		ProcedureStmt, PurgeStmt,
 		RecipeStmt, RemoveAggrStmt, RemoveOperStmt, RemoveFuncStmt, RemoveStmt,
@@ -119,7 +120,7 @@ static char *FlattenStringList(List *list);
 
 %type <node>	SubSelect
 %type <str>		join_expr, join_outer, join_spec
-%type <boolean> TriggerActionTime, TriggerForSpec
+%type <boolean> TriggerActionTime, TriggerForSpec, PLangTrusted
 
 %type <str>		TriggerEvents, TriggerFuncArg
 
@@ -225,9 +226,9 @@ static char *FlattenStringList(List *list);
 /* Keywords (in SQL92 reserved words) */
 %token	ACTION, ADD, ALL, ALTER, AND, AS, ASC,
 		BEGIN_TRANS, BETWEEN, BOTH, BY,
-		CASCADE, CAST, CHAR, CHARACTER, CHECK, CLOSE,
-		COLLATE, COLUMN, COMMIT, CONSTRAINT, CREATE, CROSS,
-		CURRENT, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
+		CASCADE, CAST, CHAR, CHARACTER, CHECK, CLOSE, COLLATE, COLUMN, COMMIT, 
+		CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, CURRENT_TIME, 
+		CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
 		DAY_P, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP,
 		END_TRANS, EXECUTE, EXISTS, EXTRACT,
 		FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
@@ -256,12 +257,12 @@ static char *FlattenStringList(List *list);
 		APPEND, ARCHIVE, ARCH_STORE,
 		BACKWARD, BEFORE, BINARY, CHANGE, CLUSTER, COPY,
 		DATABASE, DELIMITERS, DO, EXPLAIN, EXTEND,
-		FORWARD, FUNCTION, HEAVY,
+		FORWARD, FUNCTION, HANDLER, HEAVY,
 		INDEX, INHERITS, INSTEAD, ISNULL,
-		LIGHT, LISTEN, LOAD, MERGE, MOVE,
-		NEW, NONE, NOTHING, OIDS, OPERATOR, PURGE,
+		LANCOMPILER, LIGHT, LISTEN, LOAD, MERGE, MOVE,
+		NEW, NONE, NOTHING, OIDS, OPERATOR, PROCEDURAL, PURGE,
 		RECIPE, RENAME, REPLACE, RESET, RETRIEVE, RETURNS, RULE,
-		SEQUENCE, SETOF, SHOW, STDIN, STDOUT, STORE,
+		SEQUENCE, SETOF, SHOW, STDIN, STDOUT, STORE, TRUSTED, 
 		VACUUM, VERBOSE, VERSION
 
 /* Special keywords, not in the query language - see the "lex" file */
@@ -318,10 +319,12 @@ stmt :	  AddAttrStmt
 		| CopyStmt
 		| CreateStmt
 		| CreateSeqStmt
+		| CreatePLangStmt
 		| CreateTrigStmt
 		| ClusterStmt
 		| DefineStmt
 		| DestroyStmt
+		| DropPLangStmt
 		| DropTrigStmt
 		| ExtendStmt
 		| ExplainStmt
@@ -857,6 +860,36 @@ OptSeqElem:		IDENT NumConst
 				}
 		;
 
+/*****************************************************************************
+ *
+ *		QUERIES :
+ *				CREATE PROCEDURAL LANGUAGE ...
+ *				DROP PROCEDURAL LANGUAGE ...
+ *
+ *****************************************************************************/
+
+CreatePLangStmt: CREATE PLangTrusted PROCEDURAL LANGUAGE Sconst 
+			HANDLER def_name LANCOMPILER Sconst
+			{
+				CreatePLangStmt *n = makeNode(CreatePLangStmt);
+				n->plname = $5;
+				n->plhandler = $7;
+				n->plcompiler = $9;
+				n->pltrusted = $2;
+				$$ = (Node *)n;
+			}
+		;
+
+PLangTrusted:		TRUSTED { $$ = TRUE; }
+			|	{ $$ = FALSE; }
+
+DropPLangStmt: DROP PROCEDURAL LANGUAGE Sconst
+			{
+				DropPLangStmt *n = makeNode(DropPLangStmt);
+				n->plname = $4;
+				$$ = (Node *)n;
+			}
+		;
 
 /*****************************************************************************
  *
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index f3f957dbb0f8896ea7a756675e1102ddfca210f5..b2bcce32d1e9fc59ab9cd979ad73f32651fa6cf6 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.20 1997/10/25 05:44:11 thomas Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.21 1997/10/28 14:56:10 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -104,6 +104,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"function", FUNCTION},
 	{"grant", GRANT},
 	{"group", GROUP},
+	{"handler", HANDLER},
 	{"having", HAVING},
 	{"heavy", HEAVY},
 	{"hour", HOUR_P},
@@ -119,6 +120,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"isnull", ISNULL},
 	{"join", JOIN},
 	{"key", KEY},
+	{"lancompiler", LANCOMPILER},
 	{"language", LANGUAGE},
 	{"leading", LEADING},
 	{"left", LEFT},
@@ -156,6 +158,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"precision", PRECISION},
 	{"primary", PRIMARY},
 	{"privileges", PRIVILEGES},
+	{"procedural", PROCEDURAL},
 	{"procedure", PROCEDURE},
 	{"public", PUBLIC},
 	{"purge", PURGE},
@@ -188,6 +191,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"trigger", TRIGGER},
 	{"trim", TRIM},
 	{"true", TRUE_P},
+	{"trusted", TRUSTED},
 	{"type", TYPE_P},
 	{"union", UNION},
 	{"unique", UNIQUE},
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 50013c8f7f1ca2901022a04029a0b77438d6030b..1fd20eb986a9c66d87736be6dc1301ee57395e1b 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.26 1997/10/25 05:34:07 thomas Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.27 1997/10/28 14:57:24 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,7 @@
 #include "commands/recipe.h"
 #include "commands/explain.h"
 #include "commands/trigger.h"
+#include "commands/proclang.h"
 
 #include "nodes/parsenodes.h"
 #include "../backend/parser/parse.h"
@@ -75,7 +76,7 @@
  * ----------------
  */
 void
-ProcessUtility(Node *parsetree,
+ProcessUtility(Node * parsetree,
 			   CommandDest dest)
 {
 	char	   *commandTag = NULL;
@@ -149,8 +150,8 @@ ProcessUtility(Node *parsetree,
 				 */
 
 				count = stmt->howMany;
-				PerformPortalFetch(portalName, forward, count, commandTag, 
-					(stmt->ismove) ? None : dest);	/* /dev/null for MOVE */
+				PerformPortalFetch(portalName, forward, count, commandTag,
+								   (stmt->ismove) ? None : dest);		/* /dev/null for MOVE */
 			}
 			break;
 
@@ -718,6 +719,23 @@ ProcessUtility(Node *parsetree,
 			DropTrigger((DropTrigStmt *) parsetree);
 			break;
 
+			/*
+			 * ************* PROCEDURAL LANGUAGE statements *****************
+			 */
+		case T_CreatePLangStmt:
+			commandTag = "CREATE";
+			CHECK_IF_ABORTED();
+
+			CreateProceduralLanguage((CreatePLangStmt *) parsetree);
+			break;
+
+		case T_DropPLangStmt:
+			commandTag = "DROP";
+			CHECK_IF_ABORTED();
+
+			DropProceduralLanguage((DropPLangStmt *) parsetree);
+			break;
+
 			/*
 			 * ******************************** default ********************************
 			 *
diff --git a/src/backend/utils/Gen_fmgrtab.sh.in b/src/backend/utils/Gen_fmgrtab.sh.in
index 1d7c8d23568ef95c5564fd4445a71fc4f8beab87..409372e8ce2f1a41346246bed0b2aa4c2bc632fb 100644
--- a/src/backend/utils/Gen_fmgrtab.sh.in
+++ b/src/backend/utils/Gen_fmgrtab.sh.in
@@ -8,7 +8,7 @@
 #
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh.in,v 1.4 1997/07/28 00:55:41 momjian Exp $
+#    $Header: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh.in,v 1.5 1997/10/28 15:02:24 vadim Exp $
 #
 # NOTES
 #    Passes any -D options on to cpp prior to generating the list
@@ -81,7 +81,7 @@ cat > $HFILE <<FuNkYfMgRsTuFf
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: Gen_fmgrtab.sh.in,v 1.4 1997/07/28 00:55:41 momjian Exp $
+ * $Id: Gen_fmgrtab.sh.in,v 1.5 1997/10/28 15:02:24 vadim Exp $
  *
  * NOTES
  *	******************************
@@ -114,6 +114,8 @@ typedef struct {
 /*
  * defined in fmgr.c
  */
+extern char *fmgr_pl(Oid func_id, int n_arguments, FmgrValues *values,
+	bool *isNull);
 extern char *fmgr_c(func_ptr user_fn, Oid func_id, int n_arguments,
 	FmgrValues *values, bool *isNull);
 extern void fmgr_info(Oid procedureId, func_ptr *function, int *nargs);
@@ -175,7 +177,7 @@ cat > $TABCFILE <<FuNkYfMgRtAbStUfF
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh.in,v 1.4 1997/07/28 00:55:41 momjian Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh.in,v 1.5 1997/10/28 15:02:24 vadim Exp $
  *
  * NOTES
  *
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 149e99888ea5de1922104e6f0be793ca084d7c14..f12e17f03d8163a9e12d4288d2549b36d4c5b510 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.9 1997/09/18 20:22:25 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.10 1997/10/28 15:03:06 vadim Exp $
  *
  * NOTES
  *	  These routines allow the parser/planner/executor to perform
@@ -57,7 +57,7 @@ extern bool AMI_OVERRIDE;		/* XXX style */
 #include "utils/syscache.h"
 #include "catalog/indexing.h"
 
-typedef HeapTuple (*ScanFunc) ();
+typedef		HeapTuple(*ScanFunc) ();
 
 /* ----------------
  *		Warning:  cacheinfo[] below is changed, then be sure and
@@ -179,7 +179,7 @@ static struct cachedesc cacheinfo[] = {
 			0,
 			0,
 		0},
-		offsetof(TypeTupleFormData, typalign) +sizeof(char),
+		offsetof(TypeTupleFormData, typalign) + sizeof(char),
 		TypeNameIndex,
 	TypeNameIndexScan},
 	{TypeRelationName,			/* TYPOID */
@@ -316,7 +316,16 @@ static struct cachedesc cacheinfo[] = {
 		0},
 		sizeof(FormData_pg_opclass),
 		NULL,
-	(ScanFunc) NULL}
+	(ScanFunc) NULL},
+	{LanguageRelationName,		/* LANOID */
+		1,
+		{ObjectIdAttributeNumber,
+			0,
+			0,
+		0},
+		offsetof(FormData_pg_language, lancompiler),
+		NULL,
+	NULL}
 };
 
 static struct catcache *SysCache[
@@ -383,7 +392,7 @@ InitCatalogCache()
  * XXX The tuple that is returned is NOT supposed to be pfree'd!
  */
 HeapTuple
-SearchSysCacheTuple(int cacheId,/* cache selection code */
+SearchSysCacheTuple(int cacheId,		/* cache selection code */
 					Datum key1,
 					Datum key2,
 					Datum key3,
@@ -562,7 +571,7 @@ SearchSysCacheGetAttribute(int cacheId,
 		: attributeLength;		/* fixed length */
 
 		tmp = (char *) palloc(size);
-		memmove(tmp, (void *)attributeValue, size);
+		memmove(tmp, (void *) attributeValue, size);
 		returnValue = (void *) tmp;
 	}
 
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 939341a3ded4eb535dc35618aaf55a759f4b362f..a225c982555e3589e93c52c7f6a73e5f462a1cd1 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.6 1997/09/08 21:49:07 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.7 1997/10/28 15:05:32 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,13 +28,69 @@
 
 #include "utils/elog.h"
 
+#include "nodes/parsenodes.h"
+#include "commands/trigger.h"
+
+
+char	   *
+fmgr_pl(Oid func_id,
+		int n_arguments,
+		FmgrValues * values,
+		bool * isNull)
+{
+	HeapTuple	procedureTuple;
+	HeapTuple	languageTuple;
+	Form_pg_proc procedureStruct;
+	Form_pg_language languageStruct;
+	func_ptr	plcall_fn;
+	int			plcall_nargs;
+
+	/* Fetch the pg_proc tuple from the syscache */
+	procedureTuple = SearchSysCacheTuple(PROOID,
+										 ObjectIdGetDatum(func_id),
+										 0, 0, 0);
+	if (!HeapTupleIsValid(procedureTuple))
+	{
+		elog(WARN, "fmgr_pl(): Cache lookup of procedure %ld failed.",
+			 ObjectIdGetDatum(func_id));
+	}
+	procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
+
+	/* Fetch the pg_language tuple from the syscache */
+	languageTuple = SearchSysCacheTuple(LANOID,
+							  ObjectIdGetDatum(procedureStruct->prolang),
+										0, 0, 0);
+	if (!HeapTupleIsValid(languageTuple))
+	{
+		elog(WARN, "fmgr_pl(): Cache lookup of language %ld for procedure %ld failed.",
+			 ObjectIdGetDatum(procedureStruct->prolang),
+			 ObjectIdGetDatum(func_id));
+	}
+	languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
+
+	/* Get the function pointer for the PL call handler */
+	fmgr_info(languageStruct->lanplcallfoid, &plcall_fn, &plcall_nargs);
+	if (plcall_fn == NULL)
+	{
+		elog(WARN, "fmgr_pl(): failed to load PL handler for procedure %ld.",
+			 ObjectIdGetDatum(func_id));
+	}
+
+	/* Call the PL handler */
+	CurrentTriggerData = NULL;
+	return (*plcall_fn) (func_id,
+						 n_arguments,
+						 values,
+						 isNull);
+}
+
 
 char	   *
 fmgr_c(func_ptr user_fn,
 	   Oid func_id,
 	   int n_arguments,
-	   FmgrValues *values,
-	   bool *isNull)
+	   FmgrValues * values,
+	   bool * isNull)
 {
 	char	   *returnValue = (char *) NULL;
 
@@ -43,11 +99,11 @@ fmgr_c(func_ptr user_fn,
 	{
 
 		/*
-		 * a NULL func_ptr denotes untrusted function (in postgres 4.2).
-		 * Untrusted functions have very limited use and is clumsy. We
-		 * just get rid of it.
+		 * a NULL func_ptr denotet untrusted function (in postgres 4.2).
+		 * Untrusted functions have very limited use and is clumsy. We now
+		 * use this feature for procedural languages.
 		 */
-		elog(WARN, "internal error: untrusted function not supported.");
+		return fmgr_pl(func_id, n_arguments, values, isNull);
 	}
 
 	switch (n_arguments)
@@ -115,12 +171,14 @@ fmgr_c(func_ptr user_fn,
 }
 
 void
-fmgr_info(Oid procedureId, func_ptr *function, int *nargs)
+fmgr_info(Oid procedureId, func_ptr * function, int *nargs)
 {
 	func_ptr	user_fn = NULL;
 	FmgrCall   *fcp;
 	HeapTuple	procedureTuple;
 	FormData_pg_proc *procedureStruct;
+	HeapTuple	languageTuple;
+	Form_pg_language languageStruct;
 	Oid			language;
 
 	if (!(fcp = fmgr_isbuiltin(procedureId)))
@@ -158,8 +216,35 @@ fmgr_info(Oid procedureId, func_ptr *function, int *nargs)
 				*nargs = procedureStruct->pronargs;
 				break;
 			default:
-				elog(WARN, "fmgr_info: function %d: unknown language %d",
-					 procedureId, language);
+
+				/*
+				 * Might be a created procedural language Lookup the
+				 * syscache for the language and check the lanispl flag If
+				 * this is the case, we return a NULL function pointer and
+				 * the number of arguments from the procedure.
+				 */
+				languageTuple = SearchSysCacheTuple(LANOID,
+							  ObjectIdGetDatum(procedureStruct->prolang),
+													0, 0, 0);
+				if (!HeapTupleIsValid(languageTuple))
+				{
+					elog(WARN, "fmgr_info: %s %ld",
+						 "Cache lookup for language %d failed",
+						 ObjectIdGetDatum(procedureStruct->prolang));
+				}
+				languageStruct = (Form_pg_language)
+					GETSTRUCT(languageTuple);
+				if (languageStruct->lanispl)
+				{
+					user_fn = (func_ptr) NULL;
+					*nargs = procedureStruct->pronargs;
+				}
+				else
+				{
+					elog(WARN, "fmgr_info: function %d: unknown language %d",
+						 procedureId, language);
+				}
+				break;
 		}
 	}
 	else
@@ -252,7 +337,7 @@ fmgr_ptr(func_ptr user_fn, Oid func_id,...)
  * to fmgr_c().
  */
 char	   *
-fmgr_array_args(Oid procedureId, int nargs, char *args[], bool *isNull)
+fmgr_array_args(Oid procedureId, int nargs, char *args[], bool * isNull)
 {
 	func_ptr	user_fn;
 	int			true_arguments;
diff --git a/src/include/catalog/pg_language.h b/src/include/catalog/pg_language.h
index 8c8490b4861d43aa09ee8e5a78a01ddcb1758c2d..d666688cb40cfced76127a85c85a9daaa880a293 100644
--- a/src/include/catalog/pg_language.h
+++ b/src/include/catalog/pg_language.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_language.h,v 1.4 1997/09/08 02:35:16 momjian Exp $
+ * $Id: pg_language.h,v 1.5 1997/10/28 15:08:05 vadim Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -33,6 +33,9 @@
 CATALOG(pg_language)
 {
 	NameData	lanname;
+	bool		lanispl;		/* Is a procedural language */
+	bool		lanpltrusted;	/* PL is trusted */
+	Oid			lanplcallfoid;	/* Call handler for PL */
 	text		lancompiler;	/* VARIABLE LENGTH FIELD */
 } FormData_pg_language;
 
@@ -47,21 +50,24 @@ typedef FormData_pg_language *Form_pg_language;
  *		compiler constants for pg_language
  * ----------------
  */
-#define Natts_pg_language				2
+#define Natts_pg_language				5
 #define Anum_pg_language_lanname		1
-#define Anum_pg_language_lancompiler	2
+#define Anum_pg_language_lanispl		2
+#define Anum_pg_language_lanpltrusted		3
+#define Anum_pg_language_lanplcallfoid		4
+#define Anum_pg_language_lancompiler		5
 
 /* ----------------
  *		initial contents of pg_language
  * ----------------
  */
 
-DATA(insert OID = 11 ( internal "n/a" ));
+DATA(insert OID = 11 ( internal f 0 0 "n/a" ));
 #define INTERNALlanguageId 11
-DATA(insert OID = 12 ( lisp "/usr/ucb/liszt" ));
-DATA(insert OID = 13 ( "C" "/bin/cc" ));
+DATA(insert OID = 12 ( lisp f 0 0 "/usr/ucb/liszt" ));
+DATA(insert OID = 13 ( "C" f 0 0 "/bin/cc" ));
 #define ClanguageId 13
-DATA(insert OID = 14 ( "sql" "postgres"));
+DATA(insert OID = 14 ( "sql" f 0 0 "postgres"));
 #define SQLlanguageId 14
 
 
diff --git a/src/include/commands/proclang.h b/src/include/commands/proclang.h
new file mode 100644
index 0000000000000000000000000000000000000000..af6182cccaba77a2a32e0a42d2c8db2826fc4fda
--- /dev/null
+++ b/src/include/commands/proclang.h
@@ -0,0 +1,17 @@
+/*-------------------------------------------------------------------------
+ *
+ * proclang.h--
+ *	  prototypes for proclang.c.
+ *
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PROCLANG_H
+#define PROCLANG_H
+
+#include <nodes/parsenodes.h>
+
+extern void CreateProceduralLanguage(CreatePLangStmt * stmt);
+extern void DropProceduralLanguage(DropPLangStmt * stmt);
+
+#endif							/* PROCLANG_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f60e39d79143084840d7b228aefe8b01e1432091..152a1cdd28e2ed548233f8ed815a33c485b4fb72 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.15 1997/09/29 06:01:44 vadim Exp $
+ * $Id: nodes.h,v 1.16 1997/10/28 15:10:37 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -185,6 +185,8 @@ typedef enum NodeTag
 	T_VariableResetStmt,
 	T_CreateTrigStmt,
 	T_DropTrigStmt,
+	T_CreatePLangStmt,
+	T_DropPLangStmt,
 
 	T_A_Expr = 700,
 	T_Attr,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0bff16711c83bed18c26bf886a6dc98ca941542f..6e9e49b1c016fa2285bc724220976e99ff87fba2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.28 1997/09/29 06:01:46 vadim Exp $
+ * $Id: parsenodes.h,v 1.29 1997/10/28 15:10:39 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,7 +64,7 @@ typedef struct Query
 	List	   *join_relation_list_;	/* list of relations generated by
 										 * joins */
 	bool		query_is_archival_;		/* archival query flag */
-} Query;
+}			Query;
 
 
 /*****************************************************************************
@@ -98,7 +98,7 @@ typedef struct ChangeACLStmt
 	struct AclItem *aclitem;
 	unsigned	modechg;
 	List	   *relNames;
-} ChangeACLStmt;
+}			ChangeACLStmt;
 
 /* ----------------------
  *		Close Portal Statement
@@ -108,7 +108,7 @@ typedef struct ClosePortalStmt
 {
 	NodeTag		type;
 	char	   *portalname;		/* name of the portal (cursor) */
-} ClosePortalStmt;
+}			ClosePortalStmt;
 
 /* ----------------------
  *		Copy Statement
@@ -123,7 +123,7 @@ typedef struct CopyStmt
 	int			direction;		/* TO or FROM */
 	char	   *filename;		/* if NULL, use stdin/stdout */
 	char	   *delimiter;		/* delimiter character, \t by default */
-} CopyStmt;
+}			CopyStmt;
 
 /* ----------------------
  *		Create Table Statement
@@ -145,19 +145,19 @@ typedef struct CreateStmt
 	int			location;		/* smgrid (-1 if none) */
 	int			archiveLoc;		/* smgrid (-1 if none) */
 	List	   *constraints;	/* list of constraints (ConstaintDef) */
-} CreateStmt;
+}			CreateStmt;
 
 typedef enum ConstrType
 {
 	CONSTR_NONE, CONSTR_CHECK	/* type of constaints */
-} ConstrType;
+}			ConstrType;
 
 typedef struct ConstraintDef
 {
 	ConstrType	type;
 	char	   *name;			/* name */
 	void	   *def;			/* definition */
-} ConstraintDef;
+}			ConstraintDef;
 
 /* ----------------------
  *		Create/Drop TRIGGER Statements
@@ -178,14 +178,35 @@ typedef struct CreateTrigStmt
 	char	   *text;			/* AS 'text' */
 	List	   *attr;			/* UPDATE OF a, b,... (NI) or NULL */
 	char	   *when;			/* WHEN 'a > 10 ...' (NI) or NULL */
-} CreateTrigStmt;
+}			CreateTrigStmt;
 
 typedef struct DropTrigStmt
 {
 	NodeTag		type;
 	char	   *trigname;		/* TRIGGER' name */
 	char	   *relname;		/* triggered relation */
-} DropTrigStmt;
+}			DropTrigStmt;
+
+
+/* ----------------------
+ *		Create/Drop PROCEDURAL LANGUAGE Statement
+ * ----------------------
+ */
+typedef struct CreatePLangStmt
+{
+	NodeTag		type;
+	char	   *plname;			/* PL name */
+	char	   *plhandler;		/* PL call handler function */
+	char	   *plcompiler;		/* lancompiler text */
+	bool		pltrusted;		/* PL is trusted */
+}			CreatePLangStmt;
+
+typedef struct DropPLangStmt
+{
+	NodeTag		type;
+	char	   *plname;			/* PL name */
+}			DropPLangStmt;
+
 
 /* ----------------------
  *		Create SEQUENCE Statement
@@ -197,7 +218,7 @@ typedef struct CreateSeqStmt
 	NodeTag		type;
 	char	   *seqname;		/* the relation to create */
 	List	   *options;
-} CreateSeqStmt;
+}			CreateSeqStmt;
 
 /* ----------------------
  *		Create Version Statement
@@ -210,7 +231,7 @@ typedef struct VersionStmt
 	int			direction;		/* FORWARD | BACKWARD */
 	char	   *fromRelname;	/* relation to create a version */
 	char	   *date;			/* date of the snapshot */
-} VersionStmt;
+}			VersionStmt;
 
 /* ----------------------
  *		Create {Operator|Type|Aggregate} Statement
@@ -222,7 +243,7 @@ typedef struct DefineStmt
 	int			defType;		/* OPERATOR|P_TYPE|AGGREGATE */
 	char	   *defname;
 	List	   *definition;		/* a list of DefElem */
-} DefineStmt;
+}			DefineStmt;
 
 /* ----------------------
  *		Drop Table Statement
@@ -233,7 +254,7 @@ typedef struct DestroyStmt
 	NodeTag		type;
 	List	   *relNames;		/* relations to be dropped */
 	bool		sequence;
-} DestroyStmt;
+}			DestroyStmt;
 
 /* ----------------------
  *		Extend Index Statement
@@ -246,7 +267,7 @@ typedef struct ExtendStmt
 	Node	   *whereClause;	/* qualifications */
 	List	   *rangetable;		/* range table, filled in by
 								 * transformStmt() */
-} ExtendStmt;
+}			ExtendStmt;
 
 /* ----------------------
  *		Begin Recipe Statement
@@ -256,7 +277,7 @@ typedef struct RecipeStmt
 {
 	NodeTag		type;
 	char	   *recipeName;		/* name of the recipe */
-} RecipeStmt;
+}			RecipeStmt;
 
 /* ----------------------
  *		Fetch Statement
@@ -269,7 +290,7 @@ typedef struct FetchStmt
 	int			howMany;		/* amount to fetch ("ALL" --> 0) */
 	char	   *portalname;		/* name of portal (cursor) */
 	bool		ismove;			/* TRUE if MOVE */
-} FetchStmt;
+}			FetchStmt;
 
 /* ----------------------
  *		Create Index Statement
@@ -288,7 +309,7 @@ typedef struct IndexStmt
 								 * transformStmt() */
 	bool	   *lossy;			/* is index lossy? */
 	bool		unique;			/* is index unique? */
-} IndexStmt;
+}			IndexStmt;
 
 /* ----------------------
  *		Create Function Statement
@@ -305,7 +326,7 @@ typedef struct ProcedureStmt
 	List	   *withClause;		/* a list of ParamString */
 	char	   *as;				/* the SQL statement or filename */
 	char	   *language;		/* C or SQL */
-} ProcedureStmt;
+}			ProcedureStmt;
 
 /* ----------------------
  *		Purge Statement
@@ -317,7 +338,7 @@ typedef struct PurgeStmt
 	char	   *relname;		/* relation to purge */
 	char	   *beforeDate;		/* purge before this date */
 	char	   *afterDate;		/* purge after this date */
-} PurgeStmt;
+}			PurgeStmt;
 
 /* ----------------------
  *		Drop Aggregate Statement
@@ -328,7 +349,7 @@ typedef struct RemoveAggrStmt
 	NodeTag		type;
 	char	   *aggname;		/* aggregate to drop */
 	char	   *aggtype;		/* for this type */
-} RemoveAggrStmt;
+}			RemoveAggrStmt;
 
 /* ----------------------
  *		Drop Function Statement
@@ -339,7 +360,7 @@ typedef struct RemoveFuncStmt
 	NodeTag		type;
 	char	   *funcname;		/* function to drop */
 	List	   *args;			/* types of the arguments */
-} RemoveFuncStmt;
+}			RemoveFuncStmt;
 
 /* ----------------------
  *		Drop Operator Statement
@@ -350,7 +371,7 @@ typedef struct RemoveOperStmt
 	NodeTag		type;
 	char	   *opname;			/* operator to drop */
 	List	   *args;			/* types of the arguments */
-} RemoveOperStmt;
+}			RemoveOperStmt;
 
 /* ----------------------
  *		Drop {Type|Index|Rule|View} Statement
@@ -361,7 +382,7 @@ typedef struct RemoveStmt
 	NodeTag		type;
 	int			removeType;		/* P_TYPE|INDEX|RULE|VIEW */
 	char	   *name;			/* name to drop */
-} RemoveStmt;
+}			RemoveStmt;
 
 /* ----------------------
  *		Alter Table Statement
@@ -376,7 +397,7 @@ typedef struct RenameStmt
 								 * the new name. Otherwise, rename this
 								 * column name. */
 	char	   *newname;		/* the new name */
-} RenameStmt;
+}			RenameStmt;
 
 /* ----------------------
  *		Create Rule Statement
@@ -391,7 +412,7 @@ typedef struct RuleStmt
 	struct Attr *object;		/* object affected */
 	bool		instead;		/* is a 'do instead'? */
 	List	   *actions;		/* the action statements */
-} RuleStmt;
+}			RuleStmt;
 
 /* ----------------------
  *		Notify Statement
@@ -401,7 +422,7 @@ typedef struct NotifyStmt
 {
 	NodeTag		type;
 	char	   *relname;		/* relation to notify */
-} NotifyStmt;
+}			NotifyStmt;
 
 /* ----------------------
  *		Listen Statement
@@ -411,7 +432,7 @@ typedef struct ListenStmt
 {
 	NodeTag		type;
 	char	   *relname;		/* relation to listen on */
-} ListenStmt;
+}			ListenStmt;
 
 /* ----------------------
  *		{Begin|Abort|End} Transaction Statement
@@ -421,7 +442,7 @@ typedef struct TransactionStmt
 {
 	NodeTag		type;
 	int			command;		/* BEGIN|END|ABORT */
-} TransactionStmt;
+}			TransactionStmt;
 
 /* ----------------------
  *		Create View Statement
@@ -432,7 +453,7 @@ typedef struct ViewStmt
 	NodeTag		type;
 	char	   *viewname;		/* name of the view */
 	Query	   *query;			/* the SQL statement */
-} ViewStmt;
+}			ViewStmt;
 
 /* ----------------------
  *		Load Statement
@@ -442,7 +463,7 @@ typedef struct LoadStmt
 {
 	NodeTag		type;
 	char	   *filename;		/* file to load */
-} LoadStmt;
+}			LoadStmt;
 
 /* ----------------------
  *		Createdb Statement
@@ -452,7 +473,7 @@ typedef struct CreatedbStmt
 {
 	NodeTag		type;
 	char	   *dbname;			/* database to create */
-} CreatedbStmt;
+}			CreatedbStmt;
 
 /* ----------------------
  *		Destroydb Statement
@@ -462,7 +483,7 @@ typedef struct DestroydbStmt
 {
 	NodeTag		type;
 	char	   *dbname;			/* database to drop */
-} DestroydbStmt;
+}			DestroydbStmt;
 
 /* ----------------------
  *		Cluster Statement (support pbrown's cluster index implementation)
@@ -473,7 +494,7 @@ typedef struct ClusterStmt
 	NodeTag		type;
 	char	   *relname;		/* relation being indexed */
 	char	   *indexname;		/* original index defined */
-} ClusterStmt;
+}			ClusterStmt;
 
 /* ----------------------
  *		Vacuum Statement
@@ -486,7 +507,7 @@ typedef struct VacuumStmt
 	bool		analyze;		/* analyze data */
 	char	   *vacrel;			/* table to vacuum */
 	List	   *va_spec;		/* columns to analyse */
-} VacuumStmt;
+}			VacuumStmt;
 
 /* ----------------------
  *		Explain Statement
@@ -497,7 +518,7 @@ typedef struct ExplainStmt
 	NodeTag		type;
 	Query	   *query;			/* the query */
 	bool		verbose;		/* print plan info */
-} ExplainStmt;
+}			ExplainStmt;
 
 /* ----------------------
  * Set Statement
@@ -509,7 +530,7 @@ typedef struct VariableSetStmt
 	NodeTag		type;
 	char	   *name;
 	char	   *value;
-} VariableSetStmt;
+}			VariableSetStmt;
 
 /* ----------------------
  * Show Statement
@@ -520,7 +541,7 @@ typedef struct VariableShowStmt
 {
 	NodeTag		type;
 	char	   *name;
-} VariableShowStmt;
+}			VariableShowStmt;
 
 /* ----------------------
  * Reset Statement
@@ -531,7 +552,7 @@ typedef struct VariableResetStmt
 {
 	NodeTag		type;
 	char	   *name;
-} VariableResetStmt;
+}			VariableResetStmt;
 
 
 /*****************************************************************************
@@ -561,7 +582,7 @@ typedef struct DeleteStmt
 	NodeTag		type;
 	char	   *relname;		/* relation to delete from */
 	Node	   *whereClause;	/* qualifications */
-} DeleteStmt;
+}			DeleteStmt;
 
 /* ----------------------
  *		Update Statement
@@ -574,7 +595,7 @@ typedef struct ReplaceStmt
 	List	   *targetList;		/* the target list (of ResTarget) */
 	Node	   *whereClause;	/* qualifications */
 	List	   *fromClause;		/* the from clause */
-} ReplaceStmt;
+}			ReplaceStmt;
 
 /* ----------------------
  *		Create Cursor Statement
@@ -591,7 +612,7 @@ typedef struct CursorStmt
 	Node	   *whereClause;	/* qualifications */
 	List	   *groupClause;	/* group by clause */
 	List	   *sortClause;		/* sort clause (a list of SortGroupBy's) */
-} CursorStmt;
+}			CursorStmt;
 
 /* ----------------------
  *		Select Statement
@@ -609,7 +630,7 @@ typedef struct RetrieveStmt
 	Node	   *havingClause;	/* having conditional-expression */
 	List	   *selectClause;	/* subselect parameters */
 	List	   *sortClause;		/* sort clause (a list of SortGroupBy's) */
-} RetrieveStmt;
+}			RetrieveStmt;
 
 
 /****************************************************************************
@@ -628,7 +649,7 @@ typedef struct SubSelect
 	Node	   *whereClause;	/* qualifications */
 	List	   *groupClause;	/* group by clause */
 	Node	   *havingClause;	/* having conditional-expression */
-} SubSelect;
+}			SubSelect;
 
 /*
  * TypeName - specifies a type in definitions
@@ -641,7 +662,7 @@ typedef struct TypeName
 	bool		setof;			/* is a set? */
 	List	   *arrayBounds;	/* array bounds */
 	int			typlen;			/* length for char() and varchar() */
-} TypeName;
+}			TypeName;
 
 /*
  * ParamNo - specifies a parameter reference
@@ -651,7 +672,7 @@ typedef struct ParamNo
 	NodeTag		type;
 	int			number;			/* the number of the parameter */
 	TypeName   *typename;		/* the typecast */
-} ParamNo;
+}			ParamNo;
 
 /*
  * A_Expr - binary expressions
@@ -702,7 +723,7 @@ typedef struct ColumnDef
 	TypeName   *typename;		/* type of column */
 	bool		is_not_null;	/* flag to NOT NULL constraint */
 	char	   *defval;			/* default value of column */
-} ColumnDef;
+}			ColumnDef;
 
 /*
  * Ident -
@@ -718,7 +739,7 @@ typedef struct Ident
 	List	   *indirection;	/* array references */
 	bool		isRel;			/* is a relation - filled in by
 								 * transformExpr() */
-} Ident;
+}			Ident;
 
 /*
  * FuncCall - a function/aggregate invocation
@@ -728,7 +749,7 @@ typedef struct FuncCall
 	NodeTag		type;
 	char	   *funcname;		/* name of function */
 	List	   *args;			/* the arguments (list of exprs) */
-} FuncCall;
+}			FuncCall;
 
 /*
  * A_Indices - array reference or bounds ([lidx:uidx] or [uidx])
@@ -751,7 +772,7 @@ typedef struct ResTarget
 	List	   *indirection;	/* array references */
 	Node	   *val;			/* the value of the result (A_Expr or
 								 * Attr) (or A_Const) */
-} ResTarget;
+}			ResTarget;
 
 /*
  * ParamString - used in with clauses
@@ -761,7 +782,7 @@ typedef struct ParamString
 	NodeTag		type;
 	char	   *name;
 	char	   *val;
-} ParamString;
+}			ParamString;
 
 /*
  * TimeRange - specifies a time range
@@ -771,7 +792,7 @@ typedef struct TimeRange
 	NodeTag		type;
 	char	   *startDate;
 	char	   *endDate;		/* snapshot if NULL */
-} TimeRange;
+}			TimeRange;
 
 /*
  * RelExpr - relation expressions
@@ -782,7 +803,7 @@ typedef struct RelExpr
 	char	   *relname;		/* the relation name */
 	bool		inh;			/* inheritance query */
 	TimeRange  *timeRange;		/* the time range */
-} RelExpr;
+}			RelExpr;
 
 /*
  * SortGroupBy - for order by clause
@@ -794,7 +815,7 @@ typedef struct SortGroupBy
 	char	   *range;
 	char	   *name;			/* name of column to sort on */
 	char	   *useOp;			/* operator to use */
-} SortGroupBy;
+}			SortGroupBy;
 
 /*
  * RangeVar - range variable, used in from clauses
@@ -804,7 +825,7 @@ typedef struct RangeVar
 	NodeTag		type;
 	RelExpr    *relExpr;		/* the relation expression */
 	char	   *name;			/* the name to be referenced (optional) */
-} RangeVar;
+}			RangeVar;
 
 /*
  * IndexElem - index parameters (used in create index)
@@ -816,7 +837,7 @@ typedef struct IndexElem
 	List	   *args;			/* if not NULL, function index */
 	char	   *class;
 	TypeName   *tname;			/* type of index's keys (optional) */
-} IndexElem;
+}			IndexElem;
 
 /*
  * DefElem -
@@ -827,7 +848,7 @@ typedef struct DefElem
 	NodeTag		type;
 	char	   *defname;
 	Node	   *arg;			/* a (Value *) or a (TypeName *) */
-} DefElem;
+}			DefElem;
 
 
 /****************************************************************************
@@ -847,7 +868,7 @@ typedef struct TargetEntry
 	Resdom	   *resdom;			/* fjoin overload this to be a list?? */
 	Fjoin	   *fjoin;
 	Node	   *expr;			/* can be a list too */
-} TargetEntry;
+}			TargetEntry;
 
 /*
  * RangeTblEntry -
@@ -873,7 +894,7 @@ typedef struct RangeTblEntry
 	bool		archive;		/* filled in by plan_archive */
 	bool		inFromCl;		/* comes from From Clause */
 	TimeQual	timeQual;		/* filled in by pg_plan */
-} RangeTblEntry;
+}			RangeTblEntry;
 
 /*
  * SortClause -
@@ -884,7 +905,7 @@ typedef struct SortClause
 	NodeTag		type;
 	Resdom	   *resdom;			/* attributes in tlist to be sorted */
 	Oid			opoid;			/* sort operators */
-} SortClause;
+}			SortClause;
 
 /*
  * GroupClause -
@@ -895,6 +916,6 @@ typedef struct GroupClause
 	NodeTag		type;
 	TargetEntry *entry;			/* attributes to group on */
 	Oid			grpOpoid;		/* the sort operator to use */
-} GroupClause;
+}			GroupClause;
 
 #endif							/* PARSENODES_H */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 0dbf0e1c397337d1e07d3151b2bf315fe5cc60a2..951e12d6f3040aa1c81fa05a093e10558ff62b25 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rel.h,v 1.12 1997/09/08 21:55:16 momjian Exp $
+ * $Id: rel.h,v 1.13 1997/10/28 15:11:43 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,7 @@ typedef struct Trigger
 	char	   *tgname;
 	Oid			tgfoid;
 	func_ptr	tgfunc;
+	func_ptr	tgplfunc;
 	int16		tgtype;
 	int16		tgnargs;
 	int16		tgattr[8];
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 54c42a198444d90c296d72f0f3c2412508caf08d..0e37771a67ea591753a5e137243d4d13bcffa80c 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: syscache.h,v 1.7 1997/09/08 21:55:17 momjian Exp $
+ * $Id: syscache.h,v 1.8 1997/10/28 15:11:45 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,6 +59,7 @@
 #define REWRITENAME		25
 #define PROSRC			26
 #define CLADEFTYPE		27
+#define LANOID			28
 
 /* ----------------
  *		struct cachedesc:		information needed for a call to InitSysCache()