diff -u src.orig/pl_comp.c src/pl_comp.c
--- src.orig/pl_comp.c	2004-11-22 10:20:24.000000000 +0000
+++ src/pl_comp.c	2004-11-22 10:58:42.465929360 +0000
@@ -783,6 +783,72 @@
 	return T_WORD;
 }
 
+/* ----------
+ * plpgsql_parse_wordexpr		Same lookup for word followed by dot.
+ *				Should only get here if it wasn't followed by
+ *				an identifier.
+ * ----------
+ */
+int
+plpgsql_parse_wordexpr(char *word)
+{
+	PLpgSQL_nsitem *ns;
+	char	   *cp[1];
+	int			save_spacescanned = plpgsql_SpaceScanned;
+
+	/* Do case conversion and word separation */
+	/* drop dot to keep converter happy */
+	word[strlen(word) - 1] = '\0';
+	plpgsql_convert_ident(word, cp, 1);
+
+	/*
+	 * Do a lookup on the compilers namestack
+	 */
+	ns = plpgsql_ns_lookup(cp[0], NULL);
+	if (ns == NULL)
+	{
+		pfree(cp[0]);
+		return T_ERROR;
+	}
+	switch (ns->itemtype)
+	{
+		case PLPGSQL_NSTYPE_REC:
+			{
+				/*
+				 * First word is a record name, so the parenthesised expr that
+				 * follows it defines the field.
+				 */
+				PLpgSQL_recfield *new;
+
+				new = malloc(sizeof(PLpgSQL_recfield));
+				memset(new, 0, sizeof(PLpgSQL_recfield));
+
+				if (plpgsql_yylex() != '(')
+					plpgsql_yyerror("expected identifier or \"(\"");
+				new->expr = plpgsql_read_expression(')', ")");
+				new->recparentno = ns->itemno;
+				new->isExpr = true;
+				new->dtype = PLPGSQL_DTYPE_RECFIELD;
+
+				plpgsql_adddatum((PLpgSQL_datum *) new);
+
+				plpgsql_yylval.scalar = (PLpgSQL_datum *) new;
+
+				plpgsql_SpaceScanned = save_spacescanned;
+
+				pfree(cp[0]);
+				return T_SCALAR;
+			}
+
+		/* TODO: deal with rows, too? */
+
+		default:
+			break;
+
+	}
+	pfree(cp[0]);
+	return T_ERROR;
+}
 
 /* ----------
  * plpgsql_parse_dblword		Same lookup for two words
@@ -855,6 +921,7 @@
 				new->dtype = PLPGSQL_DTYPE_RECFIELD;
 				new->fieldname = strdup(cp[1]);
 				new->recparentno = ns->itemno;
+				new->isExpr = false;
 
 				plpgsql_adddatum((PLpgSQL_datum *) new);
 
@@ -901,6 +968,89 @@
 	return T_ERROR;
 }
 
+/* ----------
+ * plpgsql_parse_dblwordexpr		Same lookup for two words
+ *					separated by a dot followed by a lone dot
+ * ----------
+ */
+int
+plpgsql_parse_dblwordexpr(char *word)
+{
+	PLpgSQL_nsitem *ns;
+	char	   *cp[2];
+	int			save_spacescanned = plpgsql_SpaceScanned;
+
+	/* Do case conversion and word separation */
+	/* drop dot to keep converter happy */
+	word[strlen(word) - 1] = '\0';
+	plpgsql_convert_ident(word, cp, 2);
+
+	/*
+	 * Lookup the first word
+	 */
+	ns = plpgsql_ns_lookup(cp[0], NULL);
+	if (ns == NULL)
+	{
+		pfree(cp[0]);
+		pfree(cp[1]);
+		return T_ERROR;
+	}
+
+	switch (ns->itemtype)
+	{
+		case PLPGSQL_NSTYPE_LABEL:
+
+			/*
+			 * First word is a label, second should be a record 
+			 */
+			ns = plpgsql_ns_lookup(cp[1], cp[0]);
+			pfree(cp[0]);
+			pfree(cp[1]);
+			if (ns == NULL)
+				return T_ERROR;
+			switch (ns->itemtype)
+			{
+				case PLPGSQL_NSTYPE_REC:
+					{
+					/*
+					 * First word is a record name, so parenthesised expr
+					 * following refers to field.
+					 */
+					PLpgSQL_recfield *new;
+
+					new = malloc(sizeof(PLpgSQL_recfield));
+					memset(new, 0, sizeof(PLpgSQL_recfield));
+
+					if (plpgsql_yylex() != '(')
+						plpgsql_yyerror("expected identifier or \"(\"");
+					new->expr = plpgsql_read_expression(')', ")");
+					new->recparentno = ns->itemno;
+					new->isExpr = true;
+					new->dtype = PLPGSQL_DTYPE_RECFIELD;
+
+					plpgsql_adddatum((PLpgSQL_datum *) new);
+
+					plpgsql_yylval.scalar = (PLpgSQL_datum *) new;
+
+					plpgsql_SpaceScanned = save_spacescanned;
+
+					return T_SCALAR;
+					}
+				default:
+					return T_ERROR;
+			}
+			break;
+
+
+		default:
+			break;
+	}
+
+	pfree(cp[0]);
+	pfree(cp[1]);
+	return T_ERROR;
+}
+
 
 /* ----------
  * plpgsql_parse_tripword		Same lookup for three words
@@ -961,6 +1111,7 @@
 				new->dtype = PLPGSQL_DTYPE_RECFIELD;
 				new->fieldname = strdup(cp[2]);
 				new->recparentno = ns->itemno;
+				new->isExpr = false;
 
 				plpgsql_adddatum((PLpgSQL_datum *) new);
 
diff -u src.orig/pl_exec.c src/pl_exec.c
--- src.orig/pl_exec.c	2004-11-22 10:20:24.000000000 +0000
+++ src/pl_exec.c	2004-11-22 10:58:42.684896097 +0000
@@ -2956,6 +2956,8 @@
 				PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) target;
 				PLpgSQL_rec *rec;
 				int			fno;
+				char		*fname;
+				bool		mustfreefname = false;
 				HeapTuple	newtup;
 				int			natts;
 				int			i;
@@ -2980,20 +2982,78 @@
 							  rec->refname),
 					   errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
 
+
+				/* Have we got an expr to deal with? */
+				fno = 0;
+				fname = recfield->fieldname;
+				if (recfield->isExpr) 
+				{
+					Datum		exprdatum;
+					Oid			exprtype;
+					bool		exprisnull;
+
+					/* Evaluate expression */
+					exprdatum = exec_eval_expr(estate, recfield->expr, 
+									&exprisnull, &exprtype);
+									
+					/* If we've got an integer, it's a field number, otherwise
+					 * it's a fieldname
+					 */
+					if (exprtype == INT4OID) {
+						fno = DatumGetInt32(exprdatum);
+						if (fno < 1) {
+							ereport(ERROR,
+									(errcode(ERRCODE_UNDEFINED_COLUMN),
+									 errmsg("field expression evaluates to a number less than 1")));
+						}
+					}
+					else {
+						fname = convert_value_to_string(
+												exprdatum, exprtype);
+						mustfreefname = true;
+						if (*fname == '\0') {
+							ereport(ERROR,
+									(errcode(ERRCODE_UNDEFINED_COLUMN),
+									 errmsg("field expression evaluates to an empty string")));
+						}
+					}
+				}
+
 				/*
 				 * Get the number of the records field to change and the
-				 * number of attributes in the tuple.
+				 * number of attributes in the tuple, if we didn't get it
+				 * above.
 				 */
-				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)));
+				if (fno < 1) 
+				{
+					fno = SPI_fnumber(rec->tupdesc, fname);
+					if (fno == SPI_ERROR_NOATTRIBUTE)
+						ereport(ERROR,
+								(errcode(ERRCODE_UNDEFINED_COLUMN),
+								 errmsg("record \"%s\" has no field \"%s\"",
+										rec->refname, fname)));
+				}
+
+				/* Free fname, if required */
+				if (mustfreefname) {
+					pfree(fname);
+				}
+
 				fno--;
 				natts = rec->tupdesc->natts;
 
 				/*
+				 * Check that fno is within range
+				 */
+				if (fno >= natts) {
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_COLUMN),
+							 errmsg("record \"%s\" has no field number \"%d\"",
+									rec->refname, fno)));
+
+				}
+
+				/*
 				 * Set up values/datums arrays for heap_formtuple.	For
 				 * all the attributes except the one we want to replace,
 				 * use the value that's in the old tuple.
@@ -3288,6 +3348,8 @@
 				PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) datum;
 				PLpgSQL_rec *rec;
 				int			fno;
+				char		*fname;
+				bool		mustfreefname = false;
 
 				rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
 				if (!HeapTupleIsValid(rec->tup))
@@ -3296,19 +3358,76 @@
 					   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)));
+				
+				/* Have we got an expr to deal with? */
+				fno = 0;
+				fname = recfield->fieldname;
+				if (recfield->isExpr) 
+				{
+					Datum		exprdatum;
+					Oid			exprtype;
+					bool		exprisnull;
+
+					/* Evaluate expression */
+					exprdatum = exec_eval_expr(estate, recfield->expr, 
+									&exprisnull, &exprtype);
+									
+				   
+					/* If we've got an integer, it's a field number, otherwise
+					 * it's a fieldname
+					 */
+					if (exprtype == INT4OID) {
+						fno = DatumGetInt32(exprdatum);
+						if (fno < 1) {
+							ereport(ERROR,
+									(errcode(ERRCODE_UNDEFINED_COLUMN),
+									 errmsg("field expression evaluates to a number less than 1")));
+						}
+					}
+					else {
+						fname = convert_value_to_string(
+												exprdatum, exprtype);
+						mustfreefname = true;
+						if (*fname == '\0') {
+							ereport(ERROR,
+									(errcode(ERRCODE_UNDEFINED_COLUMN),
+									 errmsg("field expression evaluates to an empty string")));
+						}
+					}
+				}
+				 
+				/*
+				 * Get the number of the records field to change and the
+				 * number of attributes in the tuple.
+				 */
+				if (fno < 1) 
+				{
+					fno = SPI_fnumber(rec->tupdesc, fname);
+					if (fno == SPI_ERROR_NOATTRIBUTE)
+						ereport(ERROR,
+								(errcode(ERRCODE_UNDEFINED_COLUMN),
+								 errmsg("record \"%s\" has no field \"%s\"",
+										rec->refname, fname)));
+				}
+				
+				/* Free up fname if required */
+				if (mustfreefname) {
+					pfree(fname);
+				}
+				
 				*typeid = SPI_gettypeid(rec->tupdesc, fno);
 				*value = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
+				if (*value == SPI_ERROR_NOATTRIBUTE)
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_COLUMN),
+							 errmsg("record \"%s\" has no field number \"%d\"",
+									rec->refname, fno)));
+					
 				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)));
+									rec->refname, fname)));
 				break;
 			}
 
Only in src: pl_exec.c.orig
diff -u src.orig/plpgsql.h src/plpgsql.h
--- src.orig/plpgsql.h	2004-11-22 10:20:24.000000000 +0000
+++ src/plpgsql.h	2004-11-22 10:58:42.737888047 +0000
@@ -269,6 +269,8 @@
 	int			dtype;
 	int			rfno;
 	char	   *fieldname;
+	bool		isExpr;			/* Use expression for field name/number */
+	PLpgSQL_expr *expr;
 	int			recparentno;	/* dno of parent record */
 } PLpgSQL_recfield;
 
@@ -675,7 +677,9 @@
 extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
 				bool forValidator);
 extern int	plpgsql_parse_word(char *word);
+extern int	plpgsql_parse_wordexpr(char *word);
 extern int	plpgsql_parse_dblword(char *word);
+extern int	plpgsql_parse_dblwordexpr(char *word);
 extern int	plpgsql_parse_tripword(char *word);
 extern int	plpgsql_parse_wordtype(char *word);
 extern int	plpgsql_parse_dblwordtype(char *word);
diff -u src.orig/scan.l src/scan.l
--- src.orig/scan.l	2004-11-22 10:20:24.000000000 +0000
+++ src/scan.l	2004-11-22 10:58:42.781881363 +0000
@@ -195,9 +195,15 @@
 {identifier}					{
 	plpgsql_error_lineno = plpgsql_scanner_lineno();
 	return plpgsql_parse_word(yytext); }
+{identifier}{space}*\.		{
+	plpgsql_error_lineno = plpgsql_scanner_lineno();
+	return plpgsql_parse_wordexpr(yytext); }
 {identifier}{space}*\.{space}*{identifier}	{
 	plpgsql_error_lineno = plpgsql_scanner_lineno();
 	return plpgsql_parse_dblword(yytext); }
+{identifier}{space}*\.{space}*{identifier}*\.		{
+	plpgsql_error_lineno = plpgsql_scanner_lineno();
+	return plpgsql_parse_dblwordexpr(yytext); }
 {identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier}	{
 	plpgsql_error_lineno = plpgsql_scanner_lineno();
 	return plpgsql_parse_tripword(yytext); }
