diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0a9a62b759f815f1ed789033bb269b993e23accc..5ce49880b5379a611c5f4b1ea75e6b81140ebb61 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.318 2006/05/18 03:18:24 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.319 2006/05/30 11:54:51 momjian Exp $ -->
 
  <chapter id="functions">
   <title>Functions and Operators</title>
@@ -1112,7 +1112,9 @@
        <entry><literal><function>substring</function>(<parameter>string</parameter> from <replaceable>pattern</replaceable>)</literal></entry>
        <entry><type>text</type></entry>
        <entry>
-        Extract substring matching POSIX regular expression
+        Extract substring matching POSIX regular expression. See
+        <xref linkend="functions-matching"> for more information on pattern
+        matching.
        </entry>
        <entry><literal>substring('Thomas' from '...$')</literal></entry>
        <entry><literal>mas</literal></entry>
@@ -1122,8 +1124,9 @@
        <entry><literal><function>substring</function>(<parameter>string</parameter> from <replaceable>pattern</replaceable> for <replaceable>escape</replaceable>)</literal></entry>
        <entry><type>text</type></entry>
        <entry>
-        Extract substring matching <acronym>SQL</acronym> regular
-        expression
+        Extract substring matching <acronym>SQL</acronym> regular expression.
+        See <xref linkend="functions-matching"> for more information on
+        pattern matching.
        </entry>
        <entry><literal>substring('Thomas' from '%#"o_a#"_' for '#')</literal></entry>
        <entry><literal>oma</literal></entry>
@@ -1420,6 +1423,18 @@
        <entry><literal>'O''Reilly'</literal></entry>
       </row>
 
+      <row>
+       <entry><literal><function>regexp_replace</function>(<parameter>string</parameter> <type>text</type>, <parameter>pattern</parameter> <type>text</type>, <parameter>replacement</parameter> <type>text</type> [,<parameter>flags</parameter> <type>text</type>])</literal></entry>
+       <entry><type>text</type></entry>
+       <entry>
+        Replace substring matching POSIX regular expression. See
+        <xref linkend="functions-matching"> for more information on pattern
+        matching.
+       </entry>
+       <entry><literal>regexp_replace('Thomas', '.[mN]a.', 'M')</literal></entry>
+       <entry><literal>ThM</literal></entry>
+      </row>
+
       <row>
        <entry><literal><function>repeat</function>(<parameter>string</parameter> <type>text</type>, <parameter>number</parameter> <type>int</type>)</literal></entry>
        <entry><type>text</type></entry>
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 2fc998ee909e8259d264fa10595d012590ea9bf5..ef5e9e3ece0c26991e7c5a09646a1514491b5696 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.90 2006/05/30 11:40:21 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.91 2006/05/30 11:54:51 momjian Exp $ -->
 
 <chapter id="plpgsql"> 
   <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@@ -879,6 +879,55 @@ SELECT merge_fields(t.*) FROM table1 t WHERE ... ;
     field in it will draw a run-time error.
    </para>
 
+   <para>
+    To obtain the values of the fields the record is made up of,
+    the record variable can be qualified with the column or field
+    name. This can be done either by literally using the column name
+    or the column name for indexing the record can be taken out of a scalar
+    variable. The syntax for this notation is Record_variable.(IndexVariable).
+    To get information about the column field names of the record, 
+    a special expression exists that returns all column names as an array: 
+    RecordVariable.(*) .
+    Thus, the RECORD can be viewed
+    as an associative array that allows for introspection of it's contents.
+    This feature is especially useful for writing generic triggers that
+    operate on records with unknown structure.
+    Here is an example procedure that shows column names and values
+    of the predefined record NEW in a trigger procedure:
+<programlisting>
+
+CREATE OR REPLACE FUNCTION show_associative_records() RETURNS TRIGGER AS $$
+	DECLARE
+		colname		TEXT;
+		colcontent	TEXT;
+		colnames	TEXT[];
+		coln		INT4;
+		coli		INT4;
+	BEGIN
+-- obtain an array with all field names of the record
+		colnames := NEW.(*);
+		RAISE NOTICE 'All column names of test record: %', colnames;
+-- show field names and contents of record
+		coli := 1;
+		coln := array_upper(colnames,1);
+		RAISE NOTICE 'Number of columns in NEW: %', coln;
+		FOR coli IN 1 .. coln LOOP
+			colname := colnames[coli];
+			colcontent := NEW.(colname);
+			RAISE NOTICE 'column % of NEW: %', quote_ident(colname), quote_literal(colcontent);
+		END LOOP;
+-- Do it with a fixed field name:
+-- will have to know the column name
+		RAISE NOTICE 'column someint of NEW: %', quote_literal(NEW.someint);
+		RETURN NULL;
+	END;
+$$ LANGUAGE plpgsql;
+--CREATE TABLE test_records (someint INT8, somestring TEXT);
+--CREATE TRIGGER tr_test_record BEFORE INSERT ON test_records FOR EACH ROW EXECUTE PROCEDURE show_associative_records();
+
+</programlisting>
+   </para>
+
    <para>
     Note that <literal>RECORD</> is not a true data type, only a placeholder.
     One should also realize that when a <application>PL/pgSQL</application>
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 673ae54ca648af2a7207a13d49bfc2736604a18d..566379da428e6e2ad884a276b9eeddac6b6d3936 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.102 2006/05/28 03:03:17 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.103 2006/05/30 11:54:51 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -884,7 +884,8 @@ plpgsql_parse_dblword(char *word)
 
 				new = palloc(sizeof(PLpgSQL_recfield));
 				new->dtype = PLPGSQL_DTYPE_RECFIELD;
-				new->fieldname = pstrdup(cp[1]);
+				new->fieldindex.fieldname = pstrdup(cp[1]);
+				new->fieldindex_flag = RECFIELD_USE_FIELDNAME;
 				new->recparentno = ns->itemno;
 
 				plpgsql_adddatum((PLpgSQL_datum *) new);
@@ -990,7 +991,8 @@ plpgsql_parse_tripword(char *word)
 
 				new = palloc(sizeof(PLpgSQL_recfield));
 				new->dtype = PLPGSQL_DTYPE_RECFIELD;
-				new->fieldname = pstrdup(cp[2]);
+				new->fieldindex.fieldname = pstrdup(cp[2]);
+				new->fieldindex_flag = RECFIELD_USE_FIELDNAME;
 				new->recparentno = ns->itemno;
 
 				plpgsql_adddatum((PLpgSQL_datum *) new);
@@ -1438,6 +1440,132 @@ plpgsql_parse_dblwordrowtype(char *word)
 	return T_DTYPE;
 }
 
+/* ----------
+ * plpgsql_parse_recindex
+ * lookup associative index into record
+ * ----------
+ */
+int
+plpgsql_parse_recindex(char *word)
+{
+	PLpgSQL_nsitem *ns1, *ns2;
+	char		*cp[2];
+	int		ret = T_ERROR;
+	char		*fieldvar;
+	int		fl;
+
+	/* Do case conversion and word separation */
+	plpgsql_convert_ident(word, cp, 2);
+	Assert(cp[1] != NULL);
+
+	/* cleanup the "(identifier)" string to "identifier" */
+	fieldvar = cp[1];
+	Assert(*fieldvar == '(');
+	++fieldvar;	/* get rid of ( */
+
+	fl = strlen(fieldvar);
+	Assert(fieldvar[fl-1] == ')');
+	fieldvar[fl-1] = 0; /* get rid of ) */
+
+	/*
+	 * Lookup the first word
+	 */
+	ns1 = plpgsql_ns_lookup(cp[0], NULL);
+	if ( ns1 == NULL )
+	{
+		pfree(cp[0]);
+		pfree(cp[1]);
+		return T_ERROR;
+	}
+
+	ns2 = plpgsql_ns_lookup(fieldvar, NULL);
+	pfree(cp[0]);
+	pfree(cp[1]);
+	if ( ns2 == NULL )	/* name lookup failed */
+		return T_ERROR;
+
+	switch (ns1->itemtype)
+	{
+		case PLPGSQL_NSTYPE_REC:
+			{
+				/*
+				 * First word is a record name, so second word must be an
+				 * variable holding the field name in this record.
+				 */
+				if ( ns2->itemtype == PLPGSQL_NSTYPE_VAR ) {
+					PLpgSQL_recfield *new;
+
+					new = palloc(sizeof(PLpgSQL_recfield));
+					new->dtype = PLPGSQL_DTYPE_RECFIELD;
+					new->fieldindex.indexvar_no = ns2->itemno;
+					new->fieldindex_flag = RECFIELD_USE_INDEX_VAR;
+					new->recparentno = ns1->itemno;
+
+					plpgsql_adddatum((PLpgSQL_datum *) new);
+
+					plpgsql_yylval.scalar = (PLpgSQL_datum *) new;
+					ret =  T_SCALAR;
+				} 
+				break;
+			}
+		default:
+			break;
+	}
+	return ret;
+} 
+
+
+/* ----------
+ * plpgsql_parse_recfieldnames
+ * create fieldnames of a record
+ * ----------
+ */
+int
+plpgsql_parse_recfieldnames(char *word)
+{
+	PLpgSQL_nsitem	*ns1;
+	char		*cp[2];
+	int		ret = T_ERROR;
+
+	/* Do case conversion and word separation */
+	plpgsql_convert_ident(word, cp, 2);
+
+	/*
+	 * Lookup the first word
+	 */
+	ns1 = plpgsql_ns_lookup(cp[0], NULL);
+	if ( ns1 == NULL )
+	{
+		pfree(cp[0]);
+		pfree(cp[1]);
+		return T_ERROR;
+	}
+
+	pfree(cp[0]);
+	pfree(cp[1]);
+
+	switch (ns1->itemtype)
+	{
+		case PLPGSQL_NSTYPE_REC:
+			{
+				PLpgSQL_recfieldproperties *new;
+
+				new = palloc(sizeof(PLpgSQL_recfieldproperties));
+				new->dtype = PLPGSQL_DTYPE_RECFIELDNAMES;
+				new->recparentno = ns1->itemno;
+				new->save_fieldnames = NULL;
+				plpgsql_adddatum((PLpgSQL_datum *) new);
+				plpgsql_yylval.scalar = (PLpgSQL_datum *) new;
+				ret =  T_SCALAR;	/* ??? */
+				break;
+			}
+		default:
+			break;
+	}
+	return ret;
+}
+
+
 /*
  * plpgsql_build_variable - build a datum-array entry of a given
  * datatype
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index d2ff1fffd52ba64d0a05aa9dc0d41d5af4385501..63ed3f852357cc613de1b26efa914aa09f8d6536 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.165 2006/05/28 03:03:17 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.166 2006/05/30 11:54:51 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -741,7 +741,7 @@ copy_plpgsql_datum(PLpgSQL_datum *datum)
 		case PLPGSQL_DTYPE_RECFIELD:
 		case PLPGSQL_DTYPE_ARRAYELEM:
 		case PLPGSQL_DTYPE_TRIGARG:
-
+		case PLPGSQL_DTYPE_RECFIELDNAMES:
 			/*
 			 * These datum records are read-only at runtime, so no need to
 			 * copy them
@@ -851,6 +851,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
 
 			case PLPGSQL_DTYPE_RECFIELD:
 			case PLPGSQL_DTYPE_ARRAYELEM:
+			case PLPGSQL_DTYPE_RECFIELDNAMES:
 				break;
 
 			default:
@@ -2179,6 +2180,8 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
 static void
 exec_eval_cleanup(PLpgSQL_execstate *estate)
 {
+	int		i;
+	ArrayType	*a;
 	/* Clear result of a full SPI_execute */
 	if (estate->eval_tuptable != NULL)
 		SPI_freetuptable(estate->eval_tuptable);
@@ -2187,6 +2190,14 @@ exec_eval_cleanup(PLpgSQL_execstate *estate)
 	/* Clear result of exec_eval_simple_expr (but keep the econtext) */
 	if (estate->eval_econtext != NULL)
 		ResetExprContext(estate->eval_econtext);
+	for ( i = 0; i < estate->ndatums; ++i ) {
+		if ( estate->datums[i]->dtype == PLPGSQL_DTYPE_RECFIELDNAMES ) {
+			a = ((PLpgSQL_recfieldproperties *)(estate->datums[i]))->save_fieldnames;
+			if ( a )
+				pfree(a);
+			((PLpgSQL_recfieldproperties *)(estate->datums[i]))->save_fieldnames = NULL;
+		}
+	}
 }
 
 
@@ -3156,7 +3167,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
 				 */
 				PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) target;
 				PLpgSQL_rec *rec;
-				int			fno;
+				int			fno = 0;
 				HeapTuple	newtup;
 				int			natts;
 				int			i;
@@ -3185,12 +3196,35 @@ exec_assign_value(PLpgSQL_execstate *estate,
 				 * Get the number of the records field to change and the
 				 * number of attributes in the tuple.
 				 */
-				fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
-				if (fno == SPI_ERROR_NOATTRIBUTE)
+				if ( recfield->fieldindex_flag == RECFIELD_USE_FIELDNAME ) {
+					fno = SPI_fnumber(rec->tupdesc, recfield->fieldindex.fieldname);
+					if (fno == SPI_ERROR_NOATTRIBUTE)
+						ereport(ERROR,
+								(errcode(ERRCODE_UNDEFINED_COLUMN),
+								 errmsg("record \"%s\" has no field \"%s\"",
+										rec->refname, recfield->fieldindex.fieldname)));
+				}
+				else if ( recfield->fieldindex_flag == RECFIELD_USE_INDEX_VAR ) {
+					PLpgSQL_var * idxvar = (PLpgSQL_var *) (estate->datums[recfield->fieldindex.indexvar_no]);
+					char * fname = convert_value_to_string(idxvar->value, idxvar->datatype->typoid);
+					if ( fname == NULL )
+						ereport(ERROR,
+								(errcode(ERRCODE_UNDEFINED_COLUMN),
+								errmsg("record \"%s\": cannot evaluate variable to record index string",
+										rec->refname)));
+					fno = SPI_fnumber(rec->tupdesc, fname);
+					pfree(fname);
+					if (fno == SPI_ERROR_NOATTRIBUTE)
+						ereport(ERROR,
+								(errcode(ERRCODE_UNDEFINED_COLUMN),
+								 errmsg("record \"%s\" has no field \"%s\"",
+										rec->refname, fname)));
+				}
+				else
 					ereport(ERROR,
-							(errcode(ERRCODE_UNDEFINED_COLUMN),
-							 errmsg("record \"%s\" has no field \"%s\"",
-									rec->refname, recfield->fieldname)));
+						(errcode(ERRCODE_UNDEFINED_COLUMN),
+						errmsg("record \"%s\": internal error",
+									rec->refname)));
 				fno--;
 				natts = rec->tupdesc->natts;
 
@@ -3510,7 +3544,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
 			{
 				PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) datum;
 				PLpgSQL_rec *rec;
-				int			fno;
+				int			fno = 0;
 
 				rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
 				if (!HeapTupleIsValid(rec->tup))
@@ -3519,22 +3553,125 @@ exec_eval_datum(PLpgSQL_execstate *estate,
 						   errmsg("record \"%s\" is not assigned yet",
 								  rec->refname),
 						   errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
-				fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
-				if (fno == SPI_ERROR_NOATTRIBUTE)
-					ereport(ERROR,
-							(errcode(ERRCODE_UNDEFINED_COLUMN),
-							 errmsg("record \"%s\" has no field \"%s\"",
-									rec->refname, recfield->fieldname)));
-				*typeid = SPI_gettypeid(rec->tupdesc, fno);
-				*value = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
-				if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
-					ereport(ERROR,
-							(errcode(ERRCODE_DATATYPE_MISMATCH),
-							 errmsg("type of \"%s.%s\" does not match that when preparing the plan",
-									rec->refname, recfield->fieldname)));
-				break;
-			}
-
+ 				if ( recfield->fieldindex_flag == RECFIELD_USE_FIELDNAME ) {
+ 					fno = SPI_fnumber(rec->tupdesc, recfield->fieldindex.fieldname);
+ 					if (fno == SPI_ERROR_NOATTRIBUTE)
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_UNDEFINED_COLUMN),
+ 								 errmsg("record \"%s\" has no field \"%s\"",
+ 										rec->refname, recfield->fieldindex.fieldname)));
+ 				}
+ 				else if ( recfield->fieldindex_flag == RECFIELD_USE_INDEX_VAR ) {
+ 					PLpgSQL_var * idxvar = (PLpgSQL_var *) (estate->datums[recfield->fieldindex.indexvar_no]);
+ 					char * fname = convert_value_to_string(idxvar->value, idxvar->datatype->typoid);
+ 					if ( fname == NULL )
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_UNDEFINED_COLUMN),
+ 								errmsg("record \"%s\": cannot evaluate variable to record index string",
+ 										rec->refname)));
+ 					fno = SPI_fnumber(rec->tupdesc, fname);
+ 					pfree(fname);
+ 					if (fno == SPI_ERROR_NOATTRIBUTE)
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_UNDEFINED_COLUMN),
+ 								 errmsg("record \"%s\" has no field \"%s\"",
+ 										rec->refname, fname)));
+ 				}
+ 				else
+ 					ereport(ERROR,
+ 						(errcode(ERRCODE_UNDEFINED_COLUMN),
+ 						errmsg("record \"%s\": internal error",
+ 								rec->refname)));
+ 
+ 				/* Do not allow typeids to become "narrowed" by InvalidOids 
+ 				causing specialized typeids from the tuple restricting the destination */
+ 				if ( expectedtypeid != InvalidOid && expectedtypeid != SPI_gettypeid(rec->tupdesc, fno) ) {
+ 					Datum cval = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
+ 					cval =   exec_simple_cast_value(cval,
+ 									SPI_gettypeid(rec->tupdesc, fno),
+ 									expectedtypeid,
+ 									-1,
+ 									*isnull);
+ 
+ 					*value = cval;
+ 					*typeid = expectedtypeid;
+ 					/* ereport(ERROR,
+ 							(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 							 errmsg("type of \"%s\" does not match that when preparing the plan",
+ 									rec->refname)));
+ 					*/
+ 				} 
+ 				else { /* expected typeid matches */
+ 					*value = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
+ 					*typeid = SPI_gettypeid(rec->tupdesc, fno);
+ 				} 
+ 				break;
+ 			}
+ 
+ 		case PLPGSQL_DTYPE_RECFIELDNAMES:
+ 			/* Construct array datum from record field names */
+ 			{
+ 				Oid			arraytypeid,
+ 							arrayelemtypeid = TEXTOID;
+ 				int16			arraytyplen,
+ 							elemtyplen;
+ 				bool			elemtypbyval;
+ 				char			elemtypalign;
+ 				ArrayType		*arrayval;
+ 				PLpgSQL_recfieldproperties * recfp = (PLpgSQL_recfieldproperties *) datum;
+ 				PLpgSQL_rec		*rec = (PLpgSQL_rec *) (estate->datums[recfp->recparentno]);
+ 				int			fc, tfc = 0;
+ 				Datum			*arrayelems;
+ 				char			*fieldname;
+ 
+ 				if (!HeapTupleIsValid(rec->tup))
+ 					ereport(ERROR,
+ 					  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					   errmsg("record \"%s\" is not assigned yet",
+ 							  rec->refname),
+ 					   errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
+ 				arrayelems = palloc(sizeof(Datum) * rec->tupdesc->natts);
+ 				arraytypeid = get_array_type(arrayelemtypeid);
+ 				arraytyplen = get_typlen(arraytypeid);
+ 				get_typlenbyvalalign(arrayelemtypeid,
+ 						     &elemtyplen,
+ 						     &elemtypbyval,
+ 						     &elemtypalign);
+ 
+ 				if ( expectedtypeid != InvalidOid && expectedtypeid != arraytypeid )
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 							 errmsg("type of \"%s\" does not match array type when preparing the plan",
+ 									rec->refname)));
+ 				for ( fc = 0; fc < rec->tupdesc->natts; ++fc ) {
+ 					fieldname = SPI_fname(rec->tupdesc, fc+1);
+ 					if ( fieldname ) {
+ 						arrayelems[fc] = DirectFunctionCall1(textin, CStringGetDatum(fieldname));
+ 						pfree(fieldname);
+ 						++tfc;
+ 					} 
+ 				} 
+ 				arrayval = construct_array(arrayelems, tfc,
+ 							 arrayelemtypeid,
+ 							 elemtyplen,
+ 							 elemtypbyval,
+ 							 elemtypalign);
+ 
+ 
+ 				/* construct_array copies data; free temp elem array */
+ 				for ( fc = 0; fc < tfc; ++fc )
+ 					pfree(DatumGetPointer(arrayelems[fc]));
+ 				pfree(arrayelems);
+ 				*value = PointerGetDatum(arrayval);
+ 				*typeid = arraytypeid;
+ 				*isnull = false;
+ 				/* need to save the pointer because otherwise it does not get freed */
+ 				if ( recfp->save_fieldnames )
+ 					pfree(recfp->save_fieldnames);
+ 				recfp->save_fieldnames = arrayval;
+ 				break;
+ 			}
+ 
 		case PLPGSQL_DTYPE_TRIGARG:
 			{
 				PLpgSQL_trigarg *trigarg = (PLpgSQL_trigarg *) datum;
@@ -3632,7 +3769,29 @@ exec_eval_expr(PLpgSQL_execstate *estate,
 	 */
 	if (expr->plan == NULL)
 		exec_prepare_plan(estate, expr);
-
+	else {
+		/*
+		 * check for any subexpressions with varying type in the expression 
+		 * currently (July 05), this is a record field of a record indexed by a variable
+		 */
+		int			i;
+		PLpgSQL_datum		*d;
+		PLpgSQL_recfield	*rf;
+		for ( i = 0; i < expr->nparams; ++i ) {
+			d = estate->datums[expr->params[i]];
+			if ( d->dtype == PLPGSQL_DTYPE_RECFIELD ) {
+				rf = (PLpgSQL_recfield *)d;
+				if ( rf->fieldindex_flag == RECFIELD_USE_INDEX_VAR )
+					break;
+			}
+		}
+		if ( i < expr->nparams ) { /* expr may change it's type */
+			/* now discard the plan and get new one */
+			SPI_freeplan(expr->plan);
+			expr->plan = NULL;
+			exec_prepare_plan(estate, expr);
+		}
+	}
 	/*
 	 * If this is a simple expression, bypass SPI and use the executor
 	 * directly
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index a6cf2bd32eb6db53ca72a56a168b93b49087355b..8eafb3e61e658504966e8ffffd73b0a656620976 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.48 2006/03/09 21:29:37 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.49 2006/05/30 11:54:51 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1044,9 +1044,13 @@ plpgsql_dumptree(PLpgSQL_function *func)
 				printf("REC %s\n", ((PLpgSQL_rec *) d)->refname);
 				break;
 			case PLPGSQL_DTYPE_RECFIELD:
-				printf("RECFIELD %-16s of REC %d\n",
-					   ((PLpgSQL_recfield *) d)->fieldname,
-					   ((PLpgSQL_recfield *) d)->recparentno);
+				if ( ((PLpgSQL_recfield *) d)->fieldindex_flag == RECFIELD_USE_FIELDNAME )
+					printf("RECFIELD %-16s of REC %d\n",
+						   ((PLpgSQL_recfield *) d)->fieldindex.fieldname,
+						   ((PLpgSQL_recfield *) d)->recparentno);
+				else
+					printf("RECFIELD Variable of REC %d\n",
+						   ((PLpgSQL_recfield *) d)->recparentno);
 				break;
 			case PLPGSQL_DTYPE_ARRAYELEM:
 				printf("ARRAYELEM of VAR %d subscript ",
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 50fe382b323294fcfdfa1854cc6125832758c825..652f7b7bde4f9041bf1b2a29f011630a46caa6cb 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.70 2006/05/28 03:03:17 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.71 2006/05/30 11:54:51 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,7 +52,8 @@ enum
 	PLPGSQL_DTYPE_RECFIELD,
 	PLPGSQL_DTYPE_ARRAYELEM,
 	PLPGSQL_DTYPE_EXPR,
-	PLPGSQL_DTYPE_TRIGARG
+	PLPGSQL_DTYPE_TRIGARG,
+	PLPGSQL_DTYPE_RECFIELDNAMES
 };
 
 /* ----------
@@ -251,10 +252,25 @@ typedef struct
 {								/* Field in record */
 	int			dtype;
 	int			rfno;
-	char	   *fieldname;
+	union {
+		char	*fieldname;
+		int	indexvar_no;		/* dno of variable holding index string */
+	} fieldindex;
+	enum {
+		RECFIELD_USE_FIELDNAME,
+		RECFIELD_USE_INDEX_VAR,
+	}	fieldindex_flag;
 	int			recparentno;	/* dno of parent record */
 } PLpgSQL_recfield;
 
+typedef struct
+{								/* Field in record */
+	int			dtype;
+	int			rfno;
+	int			recparentno;			/* dno of parent record */
+	ArrayType *		save_fieldnames;
+} PLpgSQL_recfieldproperties;
+
 
 typedef struct
 {								/* Element of array variable */
@@ -661,6 +677,8 @@ extern int	plpgsql_parse_dblwordtype(char *word);
 extern int	plpgsql_parse_tripwordtype(char *word);
 extern int	plpgsql_parse_wordrowtype(char *word);
 extern int	plpgsql_parse_dblwordrowtype(char *word);
+extern int	plpgsql_parse_recfieldnames(char *word);
+extern int	plpgsql_parse_recindex(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(const char *refname, int lineno,
diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l
index 835162445d57316a05b1948e5f78abc8a1bbca41..c537fba96e323d3352f91ad610acf33f49921868 100644
--- a/src/pl/plpgsql/src/scan.l
+++ b/src/pl/plpgsql/src/scan.l
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.45 2006/03/09 21:29:38 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.46 2006/05/30 11:54:51 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -222,6 +222,12 @@ dump			{ return O_DUMP;			}
 {param}{space}*\.{space}*{identifier}{space}*%ROWTYPE	{
 	plpgsql_error_lineno = plpgsql_scanner_lineno();
 	return plpgsql_parse_dblwordrowtype(yytext); }
+{identifier}{space}*\.\(\*\)		{
+	plpgsql_error_lineno = plpgsql_scanner_lineno();
+	return plpgsql_parse_recfieldnames(yytext); }
+{identifier}{space}*\.\({identifier}\)		{
+	plpgsql_error_lineno = plpgsql_scanner_lineno();
+	return plpgsql_parse_recindex(yytext); }
 
 {digit}+		{ return T_NUMBER;			}
 
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 6e6597dadb2f452a7880e74c9295101efb570810..e20d76c0aeb33c28a745845a50f2a867643169be 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -2725,6 +2725,44 @@ end;
 $$ language plpgsql;
 ERROR:  end label "outer_label" specified for unlabelled block
 CONTEXT:  compile of PL/pgSQL function "end_label4" near line 5
+-- check introspective records
+create table ritest (i INT4, t TEXT);
+insert into ritest (i, t) VALUES (1, 'sometext');
+create function test_record() returns void as $$
+declare
+  cname text;
+  tval  text;
+  ival  int4;
+  tval2 text;
+  ival2 int4;
+  columns text[];
+  r     RECORD;
+begin
+  SELECT INTO r * FROM ritest WHERE i = 1;
+  ival := r.i;
+  tval := r.t;
+  RAISE NOTICE 'ival=%, tval=%', ival, tval;
+  cname := 'i';
+  ival2 := r.(cname);
+  cname :='t';
+  tval2 := r.(cname);
+  RAISE NOTICE 'ival2=%, tval2=%', ival2, tval2;
+  columns := r.(*);
+  RAISE NOTICE 'fieldnames=%', columns;
+  RETURN;
+end;
+$$ language plpgsql;
+select test_record();
+NOTICE:  ival=1, tval=sometext
+NOTICE:  ival2=1, tval2=sometext
+NOTICE:  fieldnames={i,t}
+ test_record 
+-------------
+ 
+ (1 row)
+
+drop table ritest;
+drop function test_record();
 -- using list of scalars in fori and fore stmts
 create function for_vect() returns void as $proc$
 <<lbl>>declare a integer; b varchar; c varchar; r record;
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 19e145be65f359bc3c3d22318cab92656e31c0f0..05460629860278135aace8ce1cef16fc38d4aa17 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -2281,6 +2281,38 @@ begin
 end;
 $$ language plpgsql;
 
+-- check introspective records
+create table ritest (i INT4, t TEXT);
+insert into ritest (i, t) VALUES (1, 'sometext');
+create function test_record() returns void as $$
+declare
+  cname text;
+  tval  text;
+  ival  int4;
+  tval2 text;
+  ival2 int4;
+  columns text[];
+  r     RECORD;
+begin
+  SELECT INTO r * FROM ritest WHERE i = 1;
+  ival := r.i;
+  tval := r.t;
+  RAISE NOTICE 'ival=%, tval=%', ival, tval;
+  cname := 'i';
+  ival2 := r.(cname);
+  cname :='t';
+  tval2 := r.(cname);
+  RAISE NOTICE 'ival2=%, tval2=%', ival2, tval2;
+  columns := r.(*);
+  RAISE NOTICE 'fieldnames=%', columns;
+  RETURN;
+end;
+$$ language plpgsql;
+select test_record();
+drop table ritest;
+drop function test_record();
+
+
 -- using list of scalars in fori and fore stmts
 create function for_vect() returns void as $proc$
 <<lbl>>declare a integer; b varchar; c varchar; r record;