diff --git a/src/backend/utils/Gen_fmgrtab.pl b/src/backend/utils/Gen_fmgrtab.pl
new file mode 100644
index 0000000000000000000000000000000000000000..6f67438d46b865e9c15eceb53b7ce97710e67b4d
--- /dev/null
+++ b/src/backend/utils/Gen_fmgrtab.pl
@@ -0,0 +1,194 @@
+#! /usr/bin/perl -w
+#-------------------------------------------------------------------------
+#
+# Gen_fmgrtab.pl
+#    Perl equivalent of Gen_fmgrtab.sh
+#
+# Usage: perl Gen_fmgrtab.pl path-to-pg_proc.h
+#
+# The reason for implementing this functionality twice is that we don't
+# require people to have perl to build from a tarball, but on the other
+# hand Windows can't deal with shell scripts.
+#
+# Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $PostgreSQL: pgsql/src/backend/utils/Gen_fmgrtab.pl,v 1.1 2008/06/23 17:54:29 tgl Exp $
+#
+#-------------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+# Collect arguments
+my $infile = shift;
+defined($infile) || die "$0: missing required argument: pg_proc.h\n";
+
+# Note: see Gen_fmgrtab.sh for detailed commentary on what this is doing
+
+# Collect column numbers for pg_proc columns we need
+my ($proname, $prolang, $proisstrict, $proretset, $pronargs, $prosrc);
+
+open(I, $infile) || die "Could not open $infile: $!";
+while (<I>)
+{
+    if (m/#define Anum_pg_proc_proname\s+(\d+)/) {
+	$proname = $1;
+    }
+    if (m/#define Anum_pg_proc_prolang\s+(\d+)/) {
+	$prolang = $1;
+    }
+    if (m/#define Anum_pg_proc_proisstrict\s+(\d+)/) {
+	$proisstrict = $1;
+    }
+    if (m/#define Anum_pg_proc_proretset\s+(\d+)/) {
+	$proretset = $1;
+    }
+    if (m/#define Anum_pg_proc_pronargs\s+(\d+)/) {
+	$pronargs = $1;
+    }
+    if (m/#define Anum_pg_proc_prosrc\s+(\d+)/) {
+	$prosrc = $1;
+    }
+}
+close(I);
+
+# Collect the raw data
+my @fmgr = ();
+
+open(I, $infile) || die "Could not open $infile: $!";
+while (<I>)
+{
+    next unless (/^DATA/);
+    s/^[^O]*OID[^=]*=[ \t]*//;
+    s/\(//;
+    s/"[^"]*"/"xxx"/g;
+    my @p = split;
+    next if ($p[$prolang] ne "12");
+    push @fmgr,
+      {
+        oid     => $p[0],
+        proname => $p[$proname],
+        strict  => $p[$proisstrict],
+        retset  => $p[$proretset],
+        nargs   => $p[$pronargs],
+        prosrc  => $p[$prosrc],
+      };
+}
+close(I);
+
+# Emit headers for both files
+open(H, '>', "$$-fmgroids.h") || die "Could not open $$-fmgroids.h: $!";
+print H 
+qq|/*-------------------------------------------------------------------------
+ *
+ * fmgroids.h
+ *    Macros that define the OIDs of built-in functions.
+ *
+ * These macros can be used to avoid a catalog lookup when a specific
+ * fmgr-callable function needs to be referenced.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * NOTES
+ *	******************************
+ *	*** DO NOT EDIT THIS FILE! ***
+ *	******************************
+ *
+ *	It has been GENERATED by $0
+ *	from $infile
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FMGROIDS_H
+#define FMGROIDS_H
+
+/*
+ *	Constant macros for the OIDs of entries in pg_proc.
+ *
+ *	NOTE: macros are named after the prosrc value, ie the actual C name
+ *	of the implementing function, not the proname which may be overloaded.
+ *	For example, we want to be able to assign different macro names to both
+ *	char_text() and name_text() even though these both appear with proname
+ *	'text'.  If the same C function appears in more than one pg_proc entry,
+ *	its equivalent macro will be defined with the lowest OID among those
+ *	entries.
+ */
+|;
+
+open(T, '>', "$$-fmgrtab.c") || die "Could not open $$-fmgrtab.c: $!";
+print T
+qq|/*-------------------------------------------------------------------------
+ *
+ * fmgrtab.c
+ *    The function manager's table of internal functions.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * NOTES
+ *
+ *	******************************
+ *	*** DO NOT EDIT THIS FILE! ***
+ *	******************************
+ *
+ *	It has been GENERATED by $0
+ *	from $infile
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "utils/fmgrtab.h"
+
+|;
+
+# Emit #define's and extern's -- only one per prosrc value
+my %seenit;
+foreach my $s (sort {$a->{oid} <=> $b->{oid}} @fmgr)
+{
+    next if $seenit{$s->{prosrc}};
+    $seenit{$s->{prosrc}} = 1;
+    print H "#define F_" . uc $s->{prosrc} . " $s->{oid}\n";
+    print T "extern Datum $s->{prosrc} (PG_FUNCTION_ARGS);\n";
+}
+
+# Create the fmgr_builtins table
+print T "\nconst FmgrBuiltin fmgr_builtins[] = {\n";
+my %bmap;
+$bmap{'t'} = 'true';
+$bmap{'f'} = 'false';
+foreach my $s (sort {$a->{oid} <=> $b->{oid}} @fmgr)
+{
+    print T
+	"  { $s->{oid}, \"$s->{prosrc}\", $s->{nargs}, $bmap{$s->{strict}}, $bmap{$s->{retset}}, $s->{prosrc} },\n";
+}
+
+# And add the file footers.
+print H "\n#endif /* FMGROIDS_H */\n";
+close(H);
+
+print T
+qq|  /* dummy entry is easier than getting rid of comma after last real one */
+  /* (not that there has ever been anything wrong with *having* a
+     comma after the last field in an array initializer) */
+  { 0, NULL, 0, false, false, NULL }
+};
+
+/* Note fmgr_nbuiltins excludes the dummy entry */
+const int fmgr_nbuiltins = (sizeof(fmgr_builtins) / sizeof(FmgrBuiltin)) - 1;
+|;
+
+close(T);
+
+# Finally, rename the completed files into place.
+rename "$$-fmgroids.h", "fmgroids.h"
+    || die "Could not rename $$-fmgroids.h to fmgroids.h: $!";
+rename "$$-fmgrtab.c", "fmgrtab.c"
+    || die "Could not rename $$-fmgrtab.c to fmgrtab.c: $!";
+
+exit 0;
diff --git a/src/backend/utils/Gen_fmgrtab.sh b/src/backend/utils/Gen_fmgrtab.sh
index 1fda8a99390d8badc5e3708fef3f4b199ac555bf..abe6840d0a9e00ff7c0cceb30dd2cd4cce8f09a8 100644
--- a/src/backend/utils/Gen_fmgrtab.sh
+++ b/src/backend/utils/Gen_fmgrtab.sh
@@ -4,12 +4,14 @@
 # Gen_fmgrtab.sh
 #    shell script to generate fmgroids.h and fmgrtab.c from pg_proc.h
 #
+# NOTE: if you change this, you need to fix Gen_fmgrtab.pl too!
+#
 # Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
 # Portions Copyright (c) 1994, Regents of the University of California
 #
 #
 # IDENTIFICATION
-#    $PostgreSQL: pgsql/src/backend/utils/Gen_fmgrtab.sh,v 1.39 2008/05/02 14:16:24 petere Exp $
+#    $PostgreSQL: pgsql/src/backend/utils/Gen_fmgrtab.sh,v 1.40 2008/06/23 17:54:29 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -70,20 +72,35 @@ TABLEFILE=fmgrtab.c
 
 trap 'echo "Caught signal." ; cleanup ; exit 1' 1 2 15
 
+#
+# Collect the column numbers of the pg_proc columns we need.  Because we will
+# be looking at data that includes the OID as the first column, add one to
+# each column number.
+#
+proname=`egrep '^#define Anum_pg_proc_proname[ 	]' $INFILE | $AWK '{print $3+1}'`
+prolang=`egrep '^#define Anum_pg_proc_prolang[ 	]' $INFILE | $AWK '{print $3+1}'`
+proisstrict=`egrep '^#define Anum_pg_proc_proisstrict[ 	]' $INFILE | $AWK '{print $3+1}'`
+proretset=`egrep '^#define Anum_pg_proc_proretset[ 	]' $INFILE | $AWK '{print $3+1}'`
+pronargs=`egrep '^#define Anum_pg_proc_pronargs[ 	]' $INFILE | $AWK '{print $3+1}'`
+prosrc=`egrep '^#define Anum_pg_proc_prosrc[ 	]' $INFILE | $AWK '{print $3+1}'`
 
 #
-# Generate the file containing raw pg_proc tuple data
-# (but only for "internal" language procedures...).
-# Basically we strip off the DATA macro call, leaving procedure OID as $1
+# Generate the file containing raw pg_proc data.  We do three things here:
+# 1. Strip off the DATA macro call, leaving procedure OID as $1
 # and all the pg_proc field values as $2, $3, etc on each line.
+# 2. Fold quoted fields to simple "xxx".  We need this because such fields
+# may contain whitespace, which would confuse awk's counting of fields.
+# Fortunately, this script doesn't need to look at any fields that might
+# need quoting, so this simple hack is sufficient.
+# 3. Select out just the rows for internal-language procedures.
 #
-# Note assumption here that prolang == $5 and INTERNALlanguageId == 12.
+# Note assumption here that INTERNALlanguageId == 12.
 #
 egrep '^DATA' $INFILE | \
-sed 	-e 's/^.*OID[^=]*=[^0-9]*//' \
-	-e 's/(//g' \
-	-e 's/[ 	]*).*$//' | \
-$AWK '$5 == "12" { print }' | \
+sed 	-e 's/^[^O]*OID[^=]*=[ 	]*//' \
+	-e 's/(//' \
+	-e 's/"[^"]*"/"xxx"/g' | \
+$AWK "\$$prolang == \"12\" { print }" | \
 sort -n > $SORTEDFILE
 
 if [ $? -ne 0 ]; then
@@ -120,7 +137,7 @@ cat > "$$-$OIDSFILE" <<FuNkYfMgRsTuFf
  *
  *-------------------------------------------------------------------------
  */
-#ifndef	$cpp_define
+#ifndef $cpp_define
 #define $cpp_define
 
 /*
@@ -136,12 +153,9 @@ cat > "$$-$OIDSFILE" <<FuNkYfMgRsTuFf
  */
 FuNkYfMgRsTuFf
 
-# Note assumption here that prosrc == $(NF-3).
-
 tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' < $SORTEDFILE | \
-$AWK '
-BEGIN	{ OFS = ""; }
-	{ if (seenit[$(NF-3)]++ == 0) print "#define F_", $(NF-3), " ", $1; }' >> "$$-$OIDSFILE"
+$AWK "{ if (seenit[\$$prosrc]++ == 0)
+	printf \"#define F_%s %s\\n\", \$$prosrc, \$1; }" >> "$$-$OIDSFILE"
 
 if [ $? -ne 0 ]; then
     cleanup
@@ -151,7 +165,7 @@ fi
 
 cat >> "$$-$OIDSFILE" <<FuNkYfMgRsTuFf
 
-#endif	/* $cpp_define */
+#endif /* $cpp_define */
 FuNkYfMgRsTuFf
 
 #
@@ -186,9 +200,8 @@ cat > "$$-$TABLEFILE" <<FuNkYfMgRtAbStUfF
 
 FuNkYfMgRtAbStUfF
 
-# Note assumption here that prosrc == $(NF-3).
-
-$AWK '{ print "extern Datum", $(NF-3), "(PG_FUNCTION_ARGS);"; }' $SORTEDFILE >> "$$-$TABLEFILE"
+$AWK "{ if (seenit[\$$prosrc]++ == 0)
+	print \"extern Datum\", \$$prosrc, \"(PG_FUNCTION_ARGS);\"; }" $SORTEDFILE >> "$$-$TABLEFILE"
 
 if [ $? -ne 0 ]; then
     cleanup
@@ -205,17 +218,14 @@ FuNkYfMgRtAbStUfF
 # Note: using awk arrays to translate from pg_proc values to fmgrtab values
 # may seem tedious, but avoid the temptation to write a quick x?y:z
 # conditional expression instead.  Not all awks have conditional expressions.
-#
-# Note assumptions here that prosrc == $(NF-3), pronargs == $13,
-# proisstrict == $10, proretset == $11
 
-$AWK 'BEGIN {
-    Bool["t"] = "true"
-    Bool["f"] = "false"
+$AWK "BEGIN {
+    Bool[\"t\"] = \"true\";
+    Bool[\"f\"] = \"false\";
 }
-{ printf ("  { %d, \"%s\", %d, %s, %s, %s },\n"), \
-	$1, $(NF-3), $13, Bool[$10], Bool[$11], $(NF-3)
-}' $SORTEDFILE >> "$$-$TABLEFILE"
+{ printf (\"  { %d, \\\"%s\\\", %d, %s, %s, %s },\\n\"),
+	\$1, \$$prosrc, \$$pronargs, Bool[\$$proisstrict], Bool[\$$proretset], \$$prosrc ;
+}" $SORTEDFILE >> "$$-$TABLEFILE"
 
 if [ $? -ne 0 ]; then
     cleanup
@@ -232,7 +242,6 @@ cat >> "$$-$TABLEFILE" <<FuNkYfMgRtAbStUfF
 
 /* Note fmgr_nbuiltins excludes the dummy entry */
 const int fmgr_nbuiltins = (sizeof(fmgr_builtins) / sizeof(FmgrBuiltin)) - 1;
-
 FuNkYfMgRtAbStUfF
 
 # We use the temporary files to avoid problems with concurrent runs
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index e257aa1445c1815870265e95b59e94373f419961..5e0fda146510f95caf8dade5a67c779d6c48b678 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -3,7 +3,7 @@ package Solution;
 #
 # Package that encapsulates a Visual C++ solution file generation
 #
-# $PostgreSQL: pgsql/src/tools/msvc/Solution.pm,v 1.41 2008/05/03 00:24:06 adunstan Exp $
+# $PostgreSQL: pgsql/src/tools/msvc/Solution.pm,v 1.42 2008/06/23 17:54:30 tgl Exp $
 #
 use Carp;
 use strict;
@@ -198,61 +198,12 @@ s{PG_VERSION_STR "[^"]+"}{__STRINGIFY(x) #x\n#define __STRINGIFY2(z) __STRINGIFY
     $self->GenerateDefFile("src\\interfaces\\ecpg\\compatlib\\compatlib.def","src\\interfaces\\ecpg\\compatlib\\exports.txt","LIBECPG_COMPAT");
     $self->GenerateDefFile("src\\interfaces\\ecpg\\pgtypeslib\\pgtypeslib.def","src\\interfaces\\ecpg\\pgtypeslib\\exports.txt","LIBPGTYPES");
 
-    if (IsNewer("src\\backend\\utils\\fmgrtab.c","src\\include\\catalog\\pg_proc.h"))
+    if (IsNewer('src\backend\utils\fmgrtab.c','src\include\catalog\pg_proc.h'))
     {
         print "Generating fmgrtab.c and fmgroids.h...\n";
-        open(I,"src\\include\\catalog\\pg_proc.h") || confess "Could not open pg_proc.h";
-        my @fmgr = ();
-        my %seenit;
-        while (<I>)
-        {
-            next unless (/^DATA/);
-            s/^.*OID[^=]*=[^0-9]*//;
-            s/\(//g;
-            s/[ \t]*\).*$//;
-            my @p = split;
-            next if ($p[4] ne "12");
-            push @fmgr,
-              {
-                oid     => $p[0],
-                proname => $p[1],
-                prosrc  => $p[$#p-3],
-                nargs   => $p[12],
-                strict  => $p[9],
-                retset  => $p[10],
-              };
-        }
-        close(I);
-
-        open(H,'>', 'src\include\utils\fmgroids.h')
-          ||confess "Could not open fmgroids.h";
-        print H
-          "/* fmgroids.h generated for Visual C++ */\n#ifndef FMGROIDS_H\n#define FMGROIDS_H\n\n";
-        open(T,">src\\backend\\utils\\fmgrtab.c") || confess "Could not open fmgrtab.c";
-        print T
-"/* fmgrtab.c generated for Visual C++ */\n#include \"postgres.h\"\n#include \"utils/fmgrtab.h\"\n\n";
-        foreach my $s (sort {$a->{oid} <=> $b->{oid}} @fmgr)
-        {
-            next if $seenit{$s->{prosrc}};
-            $seenit{$s->{prosrc}} = 1;
-            print H "#define F_" . uc $s->{prosrc} . " $s->{oid}\n";
-            print T "extern Datum $s->{prosrc} (PG_FUNCTION_ARGS);\n";
-        }
-        print H "\n#endif\n /* FMGROIDS_H */\n";
-        close(H);
-        print T "const FmgrBuiltin fmgr_builtins[] = {\n";
-        my %bmap;
-        $bmap{'t'} = 'true';
-        $bmap{'f'} = 'false';
-        foreach my $s (sort {$a->{oid} <=> $b->{oid}} @fmgr)
-        {
-            print T
-"  { $s->{oid}, \"$s->{prosrc}\", $s->{nargs}, $bmap{$s->{strict}}, $bmap{$s->{retset}}, $s->{prosrc} },\n";
-        }
-
-        print T
-" { 0, NULL, 0, false, false, NULL }\n};\n\nconst int fmgr_nbuiltins = (sizeof(fmgr_builtins) / sizeof(FmgrBuiltin)) - 1;\n";
-        close(T);
+        chdir('src\backend\utils');
+        system("perl Gen_fmgrtab.pl ../../../src/include/catalog/pg_proc.h");
+        chdir('..\..\..');
     }
 
     if (IsNewer('src\include\utils\probes.h','src\backend\utils\pg_trace.d'))