diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 4a8aea4d3a9e29956e4b7ad19b4a20bbfee356c0..a6a96c180dfb851f4aebff31073c84f9f81929b4 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.113 2008/11/20 20:45:29 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.114 2008/11/28 14:26:58 mha Exp $ -->
 
 <chapter id="client-authentication">
  <title>Client Authentication</title>
@@ -595,6 +595,19 @@ local   db1,db2,@demodbs  all                         md5
    There is no restriction regarding how many database users a given
    operating system user can correspond to, nor vice versa.
   </para>
+  <para>
+   If the <replaceable>system-username</> field starts with a slash (<literal>/</>),
+   the contents of the field is treated as a regular expression. This regular
+   expression supports a single capture, which can be back-referenced as
+   <literal>\1</> (backslash-one). This allows the mapping of different syntax
+   names with a single line.
+   <programlisting>
+mymap   /(.*)@mydomain.com   \1
+mymap   /(.*)@otherdomain.com guest
+   </programlisting>
+   will "remove" the domain part for users with system usernames @mydomain.com, and
+   allow all users from @otherdomain.com to log in as guest.
+  </para>
 
   <para>
    The <filename>pg_ident.conf</filename> file is read on start-up and
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index a70d53a0e2d6d6884b50c1df24dc6969e9da95ec..d6bafbca0fe1aa7c6a8e9a1591d269f835ae6ab6 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.175 2008/11/20 20:45:30 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.176 2008/11/28 14:26:58 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,6 +27,7 @@
 
 #include "libpq/ip.h"
 #include "libpq/libpq.h"
+#include "regex/regex.h"
 #include "storage/fd.h"
 #include "utils/flatfiles.h"
 #include "utils/guc.h"
@@ -1403,20 +1404,128 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
 	token = lfirst(line_item);
 	file_pgrole = token;
 
+	if (strcmp(file_map, usermap_name) != 0)
+		/* Line does not match the map name we're looking for, so just abort */
+		return;
+
 	/* Match? */
-	if (case_insensitive)
+	if (file_ident_user[0] == '/')
 	{
-		if (strcmp(file_map, usermap_name) == 0 &&
-			pg_strcasecmp(file_pgrole, pg_role) == 0 &&
-			pg_strcasecmp(file_ident_user, ident_user) == 0)
-			*found_p = true;
+		/*
+		 * When system username starts with a slash, treat it as a regular expression.
+		 * In this case, we process the system username as a regular expression that
+		 * returns exactly one match. This is replaced for \1 in the database username
+		 * string, if present.
+		 */
+		int			r;
+		regex_t		re;
+		regmatch_t	matches[2];
+		pg_wchar   *wstr;
+		int			wlen;
+		char	   *ofs;
+		char	   *regexp_pgrole;
+
+		wstr = palloc((strlen(file_ident_user+1) + 1) * sizeof(pg_wchar));
+		wlen = pg_mb2wchar_with_len(file_ident_user+1, wstr, strlen(file_ident_user+1));
+
+		/*
+		 * XXX: Major room for optimization: regexps could be compiled when the file is loaded
+		 * and then re-used in every connection.
+		 */
+		r = pg_regcomp(&re, wstr, wlen, REG_ADVANCED);
+		if (r)
+		{
+			char errstr[100];
+
+			pg_regerror(r, &re, errstr, sizeof(errstr));
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
+					 errmsg("invalid regular expression '%s': %s", file_ident_user+1, errstr)));
+
+			pfree(wstr);
+			*error_p = true;
+			return;
+		}
+		pfree(wstr);
+
+		wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
+		wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
+
+		r = pg_regexec(&re, wstr, wlen, 0, NULL, 2, matches,0);
+		if (r)
+		{
+			char errstr[100];
+
+			if (r != REG_NOMATCH)
+			{
+				/* REG_NOMATCH is not an error, everything else is */
+				pg_regerror(r, &re, errstr, sizeof(errstr));
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
+						 errmsg("regular expression match for '%s' failed: %s", file_ident_user+1, errstr)));
+				*error_p = true;
+			}
+
+			pfree(wstr);
+			pg_regfree(&re);
+			return;
+		}
+		pfree(wstr);
+
+		if ((ofs = strstr(file_pgrole, "\\1")) != NULL)
+		{
+			/* substitution of the first argument requested */
+			if (matches[1].rm_so < 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
+						 errmsg("regular expression '%s' has no subexpressions as requested by backreference in '%s'",
+								file_ident_user+1, file_pgrole)));
+			/* length: original length minus length of \1 plus length of match plus null terminator */
+			regexp_pgrole = palloc0(strlen(file_pgrole) - 2 + (matches[1].rm_eo-matches[1].rm_so) + 1);
+			strncpy(regexp_pgrole, file_pgrole, (ofs-file_pgrole));
+			memcpy(regexp_pgrole+strlen(regexp_pgrole),
+				   ident_user+matches[1].rm_so,
+				   matches[1].rm_eo-matches[1].rm_so);
+			strcat(regexp_pgrole, ofs+2);
+		}
+		else
+		{
+			/* no substitution, so copy the match */
+			regexp_pgrole = pstrdup(file_pgrole);
+		}
+
+		pg_regfree(&re);
+
+		/* now check if the username actually matched what the user is trying to connect as */
+		if (case_insensitive)
+		{
+			if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
+				*found_p = true;
+		}
+		else
+		{
+			if (strcmp(regexp_pgrole, pg_role) == 0)
+				*found_p = true;
+		}
+		pfree(regexp_pgrole);
+
+		return;
 	}
 	else
 	{
-		if (strcmp(file_map, usermap_name) == 0 &&
-			strcmp(file_pgrole, pg_role) == 0 &&
-			strcmp(file_ident_user, ident_user) == 0)
-			*found_p = true;
+		/* Not regular expression, so make complete match */
+		if (case_insensitive)
+		{
+			if (pg_strcasecmp(file_pgrole, pg_role) == 0 &&
+				pg_strcasecmp(file_ident_user, ident_user) == 0)
+				*found_p = true;
+		}
+		else
+		{
+			if (strcmp(file_pgrole, pg_role) == 0 &&
+				strcmp(file_ident_user, ident_user) == 0)
+				*found_p = true;
+		}
 	}
 
 	return;
diff --git a/src/backend/libpq/pg_ident.conf.sample b/src/backend/libpq/pg_ident.conf.sample
index 4471d56e13e76766e73a1dccc20ffe96e733273c..f673bb3b025f5ffcb35ac505a0da938ace6d1e8d 100644
--- a/src/backend/libpq/pg_ident.conf.sample
+++ b/src/backend/libpq/pg_ident.conf.sample
@@ -17,8 +17,13 @@
 # pg_hba.conf.  SYSTEM-USERNAME is the detected user name of the
 # client.  PG-USERNAME is the requested PostgreSQL user name.  The
 # existence of a record specifies that SYSTEM-USERNAME may connect as
-# PG-USERNAME.  Multiple maps may be specified in this file and used
-# by pg_hba.conf.
+# PG-USERNAME.
+#
+# If SYSTEM-USERNAME starts with a slash (/), it will be treated as
+# a regular expression. Up to one capture is allowed, and this will
+# be replaced in PG-USERNAME for backslash-one (\1) if present.
+#
+# Multiple maps may be specified in this file and used # by pg_hba.conf.
 #
 # This file is read on server startup and when the postmaster receives
 # a SIGHUP signal.  If you edit the file on a running system, you have