summaryrefslogtreecommitdiffstats
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/PerfectHash.pm380
-rw-r--r--src/tools/RELEASE_CHANGES248
-rwxr-xr-xsrc/tools/ccsym4
-rwxr-xr-xsrc/tools/check_bison_recursion.pl90
-rw-r--r--src/tools/ci/README67
-rwxr-xr-xsrc/tools/ci/cores_backtrace.sh50
-rwxr-xr-xsrc/tools/ci/gcp_freebsd_repartition.sh28
-rw-r--r--src/tools/ci/pg_ci_base.conf14
-rw-r--r--src/tools/ci/windows_build_config.pl13
-rwxr-xr-xsrc/tools/codelines7
-rwxr-xr-xsrc/tools/copyright.pl75
-rw-r--r--src/tools/editors/emacs.samples91
-rw-r--r--src/tools/editors/vim.samples17
-rwxr-xr-xsrc/tools/find_badmacros22
-rwxr-xr-xsrc/tools/find_static50
-rwxr-xr-xsrc/tools/find_typedef53
-rw-r--r--src/tools/fix-old-flex-code.pl66
-rw-r--r--src/tools/gen_keywordlist.pl197
-rw-r--r--src/tools/git-external-diff59
-rwxr-xr-xsrc/tools/git_changelog419
-rw-r--r--src/tools/ifaddrs/.gitignore1
-rw-r--r--src/tools/ifaddrs/Makefile28
-rw-r--r--src/tools/ifaddrs/README12
-rw-r--r--src/tools/ifaddrs/test_ifaddrs.c73
-rwxr-xr-xsrc/tools/make_ctags53
-rwxr-xr-xsrc/tools/make_etags16
-rwxr-xr-xsrc/tools/make_mkid11
-rwxr-xr-xsrc/tools/mark_pgdllimport.pl78
-rw-r--r--src/tools/msvc/.gitignore3
-rw-r--r--src/tools/msvc/Install.pm779
-rw-r--r--src/tools/msvc/MSBuildProject.pm533
-rw-r--r--src/tools/msvc/Mkvcbuild.pm1204
-rw-r--r--src/tools/msvc/Project.pm494
-rw-r--r--src/tools/msvc/README99
-rw-r--r--src/tools/msvc/Solution.pm1378
-rw-r--r--src/tools/msvc/VSObjectFactory.pm182
-rwxr-xr-xsrc/tools/msvc/build.bat6
-rw-r--r--src/tools/msvc/build.pl92
-rwxr-xr-xsrc/tools/msvc/clean.bat142
-rw-r--r--src/tools/msvc/config_default.pl32
-rw-r--r--src/tools/msvc/dummylib/README13
-rw-r--r--src/tools/msvc/dummylib/Win32.pm7
-rw-r--r--src/tools/msvc/dummylib/Win32/Registry.pm16
-rw-r--r--src/tools/msvc/dummylib/Win32API/File.pm17
-rw-r--r--src/tools/msvc/ecpg_regression.proj64
-rw-r--r--src/tools/msvc/gendef.pl181
-rw-r--r--src/tools/msvc/install.bat6
-rwxr-xr-xsrc/tools/msvc/install.pl39
-rw-r--r--src/tools/msvc/mkvcbuild.pl31
-rwxr-xr-xsrc/tools/msvc/pgbison.bat7
-rw-r--r--src/tools/msvc/pgbison.pl55
-rwxr-xr-xsrc/tools/msvc/pgflex.bat7
-rw-r--r--src/tools/msvc/pgflex.pl108
-rw-r--r--src/tools/msvc/vcregress.bat6
-rw-r--r--src/tools/msvc/vcregress.pl660
-rw-r--r--src/tools/perlcheck/find_perl_files14
-rw-r--r--src/tools/perlcheck/perlcriticrc31
-rwxr-xr-xsrc/tools/perlcheck/pgperlcritic20
-rwxr-xr-xsrc/tools/perlcheck/pgperlsyncheck16
-rw-r--r--src/tools/pginclude/README103
-rwxr-xr-xsrc/tools/pginclude/cpluspluscheck175
-rwxr-xr-xsrc/tools/pginclude/headerscheck159
-rwxr-xr-xsrc/tools/pginclude/pgcheckdefines305
-rwxr-xr-xsrc/tools/pginclude/pgcompinclude47
-rwxr-xr-xsrc/tools/pginclude/pgdefine25
-rwxr-xr-xsrc/tools/pginclude/pgfixinclude21
-rwxr-xr-xsrc/tools/pginclude/pgrminclude149
-rw-r--r--src/tools/pgindent/README159
-rw-r--r--src/tools/pgindent/exclude_file_patterns49
-rw-r--r--src/tools/pgindent/perltidyrc16
-rwxr-xr-xsrc/tools/pgindent/pgindent436
-rw-r--r--src/tools/pgindent/pgindent.man40
-rwxr-xr-xsrc/tools/pgindent/pgperltidy12
-rw-r--r--src/tools/pgindent/typedefs.list3900
-rwxr-xr-xsrc/tools/pgtest57
-rw-r--r--src/tools/testint128.c170
-rw-r--r--src/tools/valgrind.supp182
-rwxr-xr-xsrc/tools/version_stamp.pl116
-rwxr-xr-xsrc/tools/win32tzlist.pl140
79 files changed, 14725 insertions, 0 deletions
diff --git a/src/tools/PerfectHash.pm b/src/tools/PerfectHash.pm
new file mode 100644
index 0000000..4ffb6bd
--- /dev/null
+++ b/src/tools/PerfectHash.pm
@@ -0,0 +1,380 @@
+#----------------------------------------------------------------------
+#
+# PerfectHash.pm
+# Perl module that constructs minimal perfect hash functions
+#
+# This code constructs a minimal perfect hash function for the given
+# set of keys, using an algorithm described in
+# "An optimal algorithm for generating minimal perfect hash functions"
+# by Czech, Havas and Majewski in Information Processing Letters,
+# 43(5):256-264, October 1992.
+# This implementation is loosely based on NetBSD's "nbperf",
+# which was written by Joerg Sonnenberger.
+#
+# The resulting hash function is perfect in the sense that if the presented
+# key is one of the original set, it will return the key's index in the set
+# (in range 0..N-1). However, the caller must still verify the match,
+# as false positives are possible. Also, the hash function may return
+# values that are out of range (negative or >= N), due to summing unrelated
+# hashtable entries. This indicates that the presented key is definitely
+# not in the set.
+#
+#
+# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/tools/PerfectHash.pm
+#
+#----------------------------------------------------------------------
+
+package PerfectHash;
+
+use strict;
+use warnings;
+
+
+# At runtime, we'll compute two simple hash functions of the input key,
+# and use them to index into a mapping table. The hash functions are just
+# multiply-and-add in uint32 arithmetic, with different multipliers and
+# initial seeds. All the complexity in this module is concerned with
+# selecting hash parameters that will work and building the mapping table.
+
+# We support making case-insensitive hash functions, though this only
+# works for a strict-ASCII interpretation of case insensitivity,
+# ie, A-Z maps onto a-z and nothing else.
+my $case_fold = 0;
+
+
+#
+# Construct a C function implementing a perfect hash for the given keys.
+# The C function definition is returned as a string.
+#
+# The keys should be passed as an array reference. They can be any set
+# of Perl strings; it is caller's responsibility that there not be any
+# duplicates. (Note that the "strings" can be binary data, but hashing
+# e.g. OIDs has endianness hazards that callers must overcome.)
+#
+# The name to use for the function is specified as the second argument.
+# It will be a global function by default, but the caller may prepend
+# "static " to the result string if it wants a static function.
+#
+# Additional options can be specified as keyword-style arguments:
+#
+# case_fold => bool
+# If specified as true, the hash function is case-insensitive, for the
+# limited idea of case-insensitivity explained above.
+#
+# fixed_key_length => N
+# If specified, all keys are assumed to have length N bytes, and the
+# hash function signature will be just "int f(const void *key)"
+# rather than "int f(const void *key, size_t keylen)".
+#
+sub generate_hash_function
+{
+ my ($keys_ref, $funcname, %options) = @_;
+
+ # It's not worth passing this around as a parameter; just use a global.
+ $case_fold = $options{case_fold} || 0;
+
+ # Try different hash function parameters until we find a set that works
+ # for these keys. The multipliers are chosen to be primes that are cheap
+ # to calculate via shift-and-add, so don't change them without care.
+ # (Commonly, random seeds are tried, but we want reproducible results
+ # from this program so we don't do that.)
+ my $hash_mult1 = 257;
+ my $hash_mult2;
+ my $hash_seed1;
+ my $hash_seed2;
+ my @subresult;
+ FIND_PARAMS:
+ for ($hash_seed1 = 0; $hash_seed1 < 10; $hash_seed1++)
+ {
+
+ for ($hash_seed2 = 0; $hash_seed2 < 10; $hash_seed2++)
+ {
+ foreach (17, 31, 127, 8191)
+ {
+ $hash_mult2 = $_; # "foreach $hash_mult2" doesn't work
+ @subresult = _construct_hash_table(
+ $keys_ref, $hash_mult1, $hash_mult2,
+ $hash_seed1, $hash_seed2);
+ last FIND_PARAMS if @subresult;
+ }
+ }
+ }
+
+ # Choke if we couldn't find a workable set of parameters.
+ die "failed to generate perfect hash" if !@subresult;
+
+ # Extract info from _construct_hash_table's result array.
+ my $elemtype = $subresult[0];
+ my @hashtab = @{ $subresult[1] };
+ my $nhash = scalar(@hashtab);
+
+ # OK, construct the hash function definition including the hash table.
+ my $f = '';
+ $f .= sprintf "int\n";
+ if (defined $options{fixed_key_length})
+ {
+ $f .= sprintf "%s(const void *key)\n{\n", $funcname;
+ }
+ else
+ {
+ $f .= sprintf "%s(const void *key, size_t keylen)\n{\n", $funcname;
+ }
+ $f .= sprintf "\tstatic const %s h[%d] = {\n\t\t", $elemtype, $nhash;
+ for (my $i = 0; $i < $nhash; $i++)
+ {
+ # Hash element.
+ $f .= sprintf "%d", $hashtab[$i];
+ next if ($i == $nhash - 1);
+
+ # Optional indentation and newline, with eight items per line.
+ $f .= sprintf ",%s",
+ ($i % 8 == 7 ? "\n\t\t" : ' ' x (6 - length($hashtab[$i])));
+ }
+ $f .= sprintf "\n" if ($nhash % 8 != 0);
+ $f .= sprintf "\t};\n\n";
+ $f .= sprintf "\tconst unsigned char *k = (const unsigned char *) key;\n";
+ $f .= sprintf "\tsize_t\t\tkeylen = %d;\n", $options{fixed_key_length}
+ if (defined $options{fixed_key_length});
+ $f .= sprintf "\tuint32\t\ta = %d;\n", $hash_seed1;
+ $f .= sprintf "\tuint32\t\tb = %d;\n\n", $hash_seed2;
+ $f .= sprintf "\twhile (keylen--)\n\t{\n";
+ $f .= sprintf "\t\tunsigned char c = *k++";
+ $f .= sprintf " | 0x20" if $case_fold; # see comment below
+ $f .= sprintf ";\n\n";
+ $f .= sprintf "\t\ta = a * %d + c;\n", $hash_mult1;
+ $f .= sprintf "\t\tb = b * %d + c;\n", $hash_mult2;
+ $f .= sprintf "\t}\n";
+ $f .= sprintf "\treturn h[a %% %d] + h[b %% %d];\n", $nhash, $nhash;
+ $f .= sprintf "}\n";
+
+ return $f;
+}
+
+
+# Calculate a hash function as the run-time code will do.
+#
+# If we are making a case-insensitive hash function, we implement that
+# by OR'ing 0x20 into each byte of the key. This correctly transforms
+# upper-case ASCII into lower-case ASCII, while not changing digits or
+# dollar signs. (It does change '_', as well as other characters not
+# likely to appear in keywords; this has little effect on the hash's
+# ability to discriminate keywords.)
+sub _calc_hash
+{
+ my ($key, $mult, $seed) = @_;
+
+ my $result = $seed;
+ for my $c (split //, $key)
+ {
+ my $cn = ord($c);
+ $cn |= 0x20 if $case_fold;
+ $result = ($result * $mult + $cn) % 4294967296;
+ }
+ return $result;
+}
+
+
+# Attempt to construct a mapping table for a minimal perfect hash function
+# for the given keys, using the specified hash parameters.
+#
+# Returns an array containing the mapping table element type name as the
+# first element, and a ref to an array of the table values as the second.
+#
+# Returns an empty array on failure; then caller should choose different
+# hash parameter(s) and try again.
+sub _construct_hash_table
+{
+ my ($keys_ref, $hash_mult1, $hash_mult2, $hash_seed1, $hash_seed2) = @_;
+ my @keys = @{$keys_ref};
+
+ # This algorithm is based on a graph whose edges correspond to the
+ # keys and whose vertices correspond to entries of the mapping table.
+ # A key's edge links the two vertices whose indexes are the outputs of
+ # the two hash functions for that key. For K keys, the mapping
+ # table must have at least 2*K+1 entries, guaranteeing that there's at
+ # least one unused entry. (In principle, larger mapping tables make it
+ # easier to find a workable hash and increase the number of inputs that
+ # can be rejected due to touching unused hashtable entries. In practice,
+ # neither effect seems strong enough to justify using a larger table.)
+ my $nedges = scalar @keys; # number of edges
+ my $nverts = 2 * $nedges + 1; # number of vertices
+
+ # However, it would be very bad if $nverts were exactly equal to either
+ # $hash_mult1 or $hash_mult2: effectively, that hash function would be
+ # sensitive to only the last byte of each key. Cases where $nverts is a
+ # multiple of either multiplier likewise lose information. (But $nverts
+ # can't actually divide them, if they've been intelligently chosen as
+ # primes.) We can avoid such problems by adjusting the table size.
+ while ($nverts % $hash_mult1 == 0
+ || $nverts % $hash_mult2 == 0)
+ {
+ $nverts++;
+ }
+
+ # Initialize the array of edges.
+ my @E = ();
+ foreach my $kw (@keys)
+ {
+ # Calculate hashes for this key.
+ # The hashes are immediately reduced modulo the mapping table size.
+ my $hash1 = _calc_hash($kw, $hash_mult1, $hash_seed1) % $nverts;
+ my $hash2 = _calc_hash($kw, $hash_mult2, $hash_seed2) % $nverts;
+
+ # If the two hashes are the same for any key, we have to fail
+ # since this edge would itself form a cycle in the graph.
+ return () if $hash1 == $hash2;
+
+ # Add the edge for this key.
+ push @E, { left => $hash1, right => $hash2 };
+ }
+
+ # Initialize the array of vertices, giving them all empty lists
+ # of associated edges. (The lists will be hashes of edge numbers.)
+ my @V = ();
+ for (my $v = 0; $v < $nverts; $v++)
+ {
+ push @V, { edges => {} };
+ }
+
+ # Insert each edge in the lists of edges connected to its vertices.
+ for (my $e = 0; $e < $nedges; $e++)
+ {
+ my $v = $E[$e]{left};
+ $V[$v]{edges}->{$e} = 1;
+
+ $v = $E[$e]{right};
+ $V[$v]{edges}->{$e} = 1;
+ }
+
+ # Now we attempt to prove the graph acyclic.
+ # A cycle-free graph is either empty or has some vertex of degree 1.
+ # Removing the edge attached to that vertex doesn't change this property,
+ # so doing that repeatedly will reduce the size of the graph.
+ # If the graph is empty at the end of the process, it was acyclic.
+ # We track the order of edge removal so that the next phase can process
+ # them in reverse order of removal.
+ my @output_order = ();
+
+ # Consider each vertex as a possible starting point for edge-removal.
+ for (my $startv = 0; $startv < $nverts; $startv++)
+ {
+ my $v = $startv;
+
+ # If vertex v is of degree 1 (i.e. exactly 1 edge connects to it),
+ # remove that edge, and then consider the edge's other vertex to see
+ # if it is now of degree 1. The inner loop repeats until reaching a
+ # vertex not of degree 1.
+ while (scalar(keys(%{ $V[$v]{edges} })) == 1)
+ {
+ # Unlink its only edge.
+ my $e = (keys(%{ $V[$v]{edges} }))[0];
+ delete($V[$v]{edges}->{$e});
+
+ # Unlink the edge from its other vertex, too.
+ my $v2 = $E[$e]{left};
+ $v2 = $E[$e]{right} if ($v2 == $v);
+ delete($V[$v2]{edges}->{$e});
+
+ # Push e onto the front of the output-order list.
+ unshift @output_order, $e;
+
+ # Consider v2 on next iteration of inner loop.
+ $v = $v2;
+ }
+ }
+
+ # We succeeded only if all edges were removed from the graph.
+ return () if (scalar(@output_order) != $nedges);
+
+ # OK, build the hash table of size $nverts.
+ my @hashtab = (0) x $nverts;
+ # We need a "visited" flag array in this step, too.
+ my @visited = (0) x $nverts;
+
+ # The goal is that for any key, the sum of the hash table entries for
+ # its first and second hash values is the desired output (i.e., the key
+ # number). By assigning hash table values in the selected edge order,
+ # we can guarantee that that's true. This works because the edge first
+ # removed from the graph (and hence last to be visited here) must have
+ # at least one vertex it shared with no other edge; hence it will have at
+ # least one vertex (hashtable entry) still unvisited when we reach it here,
+ # and we can assign that unvisited entry a value that makes the sum come
+ # out as we wish. By induction, the same holds for all the other edges.
+ foreach my $e (@output_order)
+ {
+ my $l = $E[$e]{left};
+ my $r = $E[$e]{right};
+ if (!$visited[$l])
+ {
+ # $hashtab[$r] might be zero, or some previously assigned value.
+ $hashtab[$l] = $e - $hashtab[$r];
+ }
+ else
+ {
+ die "oops, doubly used hashtab entry" if $visited[$r];
+ # $hashtab[$l] might be zero, or some previously assigned value.
+ $hashtab[$r] = $e - $hashtab[$l];
+ }
+ # Now freeze both of these hashtab entries.
+ $visited[$l] = 1;
+ $visited[$r] = 1;
+ }
+
+ # Detect range of values needed in hash table.
+ my $hmin = $nedges;
+ my $hmax = 0;
+ for (my $v = 0; $v < $nverts; $v++)
+ {
+ $hmin = $hashtab[$v] if $hashtab[$v] < $hmin;
+ $hmax = $hashtab[$v] if $hashtab[$v] > $hmax;
+ }
+
+ # Choose width of hashtable entries. In addition to the actual values,
+ # we need to be able to store a flag for unused entries, and we wish to
+ # have the property that adding any other entry value to the flag gives
+ # an out-of-range result (>= $nedges).
+ my $elemtype;
+ my $unused_flag;
+
+ if ( $hmin >= -0x7F
+ && $hmax <= 0x7F
+ && $hmin + 0x7F >= $nedges)
+ {
+ # int8 will work
+ $elemtype = 'int8';
+ $unused_flag = 0x7F;
+ }
+ elsif ($hmin >= -0x7FFF
+ && $hmax <= 0x7FFF
+ && $hmin + 0x7FFF >= $nedges)
+ {
+ # int16 will work
+ $elemtype = 'int16';
+ $unused_flag = 0x7FFF;
+ }
+ elsif ($hmin >= -0x7FFFFFFF
+ && $hmax <= 0x7FFFFFFF
+ && $hmin + 0x3FFFFFFF >= $nedges)
+ {
+ # int32 will work
+ $elemtype = 'int32';
+ $unused_flag = 0x3FFFFFFF;
+ }
+ else
+ {
+ die "hash table values too wide";
+ }
+
+ # Set any unvisited hashtable entries to $unused_flag.
+ for (my $v = 0; $v < $nverts; $v++)
+ {
+ $hashtab[$v] = $unused_flag if !$visited[$v];
+ }
+
+ return ($elemtype, \@hashtab);
+}
+
+1;
diff --git a/src/tools/RELEASE_CHANGES b/src/tools/RELEASE_CHANGES
new file mode 100644
index 0000000..e8de724
--- /dev/null
+++ b/src/tools/RELEASE_CHANGES
@@ -0,0 +1,248 @@
+For All Releases (major, minor, beta, RC)
+================
+
+* Release version number changes
+ o run src/tools/version_stamp.pl, then run autoconf
+ (by packager) (beta)
+
+* Release notes
+ o run git log and, if useful, src/tools/git_changelog
+ o update doc/src/sgml/release-NN.sgml in relevant branches
+ o run spellchecker on result
+ o add SGML markup
+
+* Update timezone data to match latest IANA timezone database and new
+ Windows releases, if any (see src/timezone/README)
+
+* Translation updates
+ 1. Check out the messages repository (of the right branch) from
+ <http://git.postgresql.org/git/pgtranslation/messages.git>.
+ 2. Check out the admin repository from
+ <http://git.postgresql.org/git/pgtranslation/admin.git>.
+ 3. From babel.postgresql.org, download the "qualified list"
+ for the respective branch.
+ 4. Run ".../admin/cp-po -L qualified-list-xxx.txt -g .../messages .../postgresql".
+ This creates a commit in the postgresql repository.
+ 5. Push everything.
+
+
+For Major Releases
+==================
+(in addition to the above)
+
+Note that once the release branch has been forked off in git,
+release-note editing happens in that branch, not in master.
+Updates to the rest of the documentation usually need to happen
+in both master and the branch.
+
+* Release notes
+ o use src/tools/git_changelog
+ o retain the relevant commits
+ o new features and options
+ o major performance improvements
+ o bug fixes for serious or common bugs
+ o incompatibilities and significant user-visible changes
+ o major source code changes
+ o update TODO list, http://wiki.postgresql.org/wiki/Todo
+ o verify items marked as completed are completed
+ o mark additional items as completed
+ o remove completed items
+ o group items into categories
+ o select incompatibilities
+ o add documentation links for items
+ o select major features
+
+* Documentation
+ o document all new features
+ o update help output from inside the programs
+ o doc/src/sgml/ref manual pages
+
+* Ports
+ o update ports list in doc/src/sgml/installation.sgml
+
+
+Pre-Beta Tasks
+==============
+
+These things should be done at least once per development cycle.
+Typically we do them between feature freeze and start of beta test,
+but there may be reasons to do them at other times as well.
+
+* Run mechanical code beautification tools:
+ pgindent, pgperltidy, and "make reformat-dat-files"
+ (complete steps from src/tools/pgindent/README)
+
+* Renumber any manually-assigned OIDs between 8000 and 9999
+ to lower numbers, using renumber_oids.pl (see notes in bki.sgml)
+
+* Update config.guess and config.sub
+ (from https://savannah.gnu.org/projects/config)
+
+* Update Unicode data: Edit UNICODE_VERSION and CLDR_VERSION in
+ src/Makefile.global.in, run make update-unicode, and commit.
+
+
+Starting a New Development Cycle
+================================
+
+* Typically, we do pgindent and perltidy runs just before branching,
+ as well as before beta (complete steps from src/tools/pgindent/README)
+
+* Create a branch in git for maintenance of the previous release
+ o on master branch, do:
+ git pull # be sure you have the latest "master"
+ git branch "new-branch-name"
+ git push -u origin "new-branch-name"
+ for example,
+ git branch REL_11_STABLE
+ git push -u origin REL_11_STABLE
+
+* Add new branch's name to list in src/tools/git_changelog
+
+* Increment the major version number in src/tools/version_stamp.pl
+
+* Run "src/tools/version_stamp.pl devel", then run autoconf
+
+* Create a new doc/src/sgml/release-NN.sgml file (initially just a
+ placeholder), "git rm" the previous one, and update release.sgml and
+ filelist.sgml to match.
+
+* Notify the private committers email list, to ensure all committers
+ are aware of the new branch even if they're not paying close attention
+ to pgsql-hackers.
+
+* Get the buildfarm's 'branches_of_interest.txt' file updated with the new
+ branch. Once the buildfarm server is accepting reports, notify the
+ buildfarm owners' email list, for the benefit of owners who use manual
+ branch scheduling.
+
+
+Creating Back-Branch Release Notes
+==================================
+
+* Run src/tools/git_changelog to generate a list of relevant commits.
+ You can also run 'git log' in each branch. Be sure to use the --since
+ branch tag and not the release date, as commits could have been done
+ between branch stamping and the release date.
+
+* Remember to include any older branch commits not in the newest branch.
+ This can be accomplished by diff'ing the newest and older branch commit
+ logs and looking for lines that only appear in the older branch, e.g.:
+
+ diff commit-N.N.log commit-O.O.log | grep '^>'
+
+* On the most recent release branch (*not* in master), edit and create SGML
+ markup for relevant changes in that branch's release-NN.sgml file.
+ Minor release notes should include more small change details because
+ testing is limited.
+
+* Copy this text into older branches' release-NN.sgml files, then remove
+ items that do not apply based on commit logs for that branch.
+
+* The minor release notes for the oldest active branch should always
+ include a warning about its impending EOL. Use the same boilerplate
+ text used in previous branches.
+
+
+Retiring a Branch
+=================
+
+* Notify the private committers email list, to ensure all committers
+ are aware of the branch being dead.
+
+* Get the buildfarm's 'branches_of_interest.txt' file updated to remove
+ the retired branch. Then notify the buildfarm owners' email list,
+ for the benefit of owners who use manual branch scheduling.
+
+
+
+---------------------------------------------------------------------------
+
+ Library Version Changes
+ =======================
+
+Major Version
+=============
+
+The major version number should be updated whenever the source of the
+library changes to make it binary incompatible. Such changes include,
+but are not limited to:
+
+1. Removing a public function or structure (or typedef, enum, ...)
+
+2. Modifying a public functions arguments.
+
+3. Removing a field from a public structure.
+
+4. Adding a field to a public structure, unless steps have been
+previously taken to shield users from such a change, for example by
+such structures only ever being allocated/instantiated by a library
+function which would give the new field a suitable default value.
+
+Adding a new function should NOT force an increase in the major version
+number. (Packagers will see the standard minor number update and install
+the new library.) When the major version is increased all applications
+which link to the library MUST be recompiled - this is not desirable.
+
+Minor Version
+=============
+
+The minor version number should be updated whenever the functionality of
+the library has changed, typically a change in source code between releases
+would mean an increase in the minor version number so long as it does not
+require a major version increase.
+
+Given that we make at least some changes to our libraries in every major
+PostgreSQL version, we always bump all minor library version numbers in
+each development cycle as a matter of policy. This is currently mechanized
+by referencing the MAJORVERSION make macro in the value of SO_MINOR_VERSION
+for each shared library. As of v10, SO_MINOR_VERSION is simply equal to
+MAJORVERSION in all cases. If we ever make an incompatible break in a
+library's API, forcing a major version bump, we could continue to increase
+SO_MINOR_VERSION (thus, perhaps, going from libpq.so.5.12 to libpq.so.6.13),
+or we could reset SO_MINOR_VERSION to zero, using makefile code along the
+lines of
+ SO_MINOR_VERSION= $(shell expr $(MAJORVERSION) - 13)
+so that the number continues to increase automatically in later branches.
+For now, that complication is not necessary.
+
+Minimizing Changes
+==================
+
+When modifying public functions arguments, steps should be taken to
+maintain binary compatibility across minor PostgreSQL releases (e.g. the
+7.2 series, the 7.3 series, the 7.4/8.0 series). Consider the following
+function:
+
+ void print_stuff(int arg1, int arg2)
+ {
+ printf("stuff: %d %d\n", arg1, arg2);
+ }
+
+If we wanted to add a third argument:
+
+ void print_stuff(int arg1, int arg2, int arg3)
+ {
+ printf("stuff: %d %d %d\n", arg1, arg2, arg3);
+ }
+
+Then doing it like this:
+
+ void print_stuff2(int arg1, int arg2, int arg3)
+ {
+ printf("stuff: %d %d %d\n", arg1, arg2, arg3);
+ }
+
+ void print_stuff(int arg1, int arg2)
+ {
+ print_stuff2(arg1, arg2, 0);
+ }
+
+would maintain binary compatibility. Obviously this would add a fair
+bit of cruft if used extensively, but considering the changes between
+minor versions would probably be worthwhile to avoid bumping library
+major version. Naturally in the next major version print_stuff() would
+assume the functionality and arguments of print_stuff2().
+
+
+Lee Kindness
diff --git a/src/tools/ccsym b/src/tools/ccsym
new file mode 100755
index 0000000..972c843
--- /dev/null
+++ b/src/tools/ccsym
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+# display gcc predefined preprocessor macros
+gcc -dM -E - < /dev/null
diff --git a/src/tools/check_bison_recursion.pl b/src/tools/check_bison_recursion.pl
new file mode 100755
index 0000000..6f748ae
--- /dev/null
+++ b/src/tools/check_bison_recursion.pl
@@ -0,0 +1,90 @@
+#! /usr/bin/perl
+
+#################################################################
+#
+# check_bison_recursion.pl -- check for right recursion in Bison grammars
+#
+# The standard way to parse list constructs in Bison grammars is via left
+# recursion, wherein a nonterminal symbol has itself as the first symbol
+# in one of its expansion rules. It is also possible to parse a list via
+# right recursion, wherein a nonterminal symbol has itself as the last
+# symbol of an expansion; but that's a bad way to write it because a long
+# enough list will result in parser stack overflow. Since Bison doesn't
+# have any built-in way to warn about use of right recursion, we use this
+# script when we want to check for the problem.
+#
+# To use: run bison with the -v switch, then feed the produced y.output
+# file to this script.
+#
+# Copyright (c) 2011-2022, PostgreSQL Global Development Group
+#
+# src/tools/check_bison_recursion.pl
+#################################################################
+
+use strict;
+use warnings;
+
+my $debug = 0;
+
+# must retain this across input lines
+my $cur_nonterminal;
+
+# We parse the input and emit warnings on the fly.
+my $in_grammar = 0;
+
+while (<>)
+{
+ my $rule_number;
+ my $rhs;
+
+ # We only care about the "Grammar" part of the input.
+ if (m/^Grammar$/)
+ {
+ $in_grammar = 1;
+ }
+ elsif (m/^Terminal/)
+ {
+ $in_grammar = 0;
+ }
+ elsif ($in_grammar)
+ {
+ if (m/^\s*(\d+)\s+(\S+):\s+(.*)$/)
+ {
+
+ # first rule for nonterminal
+ $rule_number = $1;
+ $cur_nonterminal = $2;
+ $rhs = $3;
+ }
+ elsif (m/^\s*(\d+)\s+\|\s+(.*)$/)
+ {
+
+ # additional rule for nonterminal
+ $rule_number = $1;
+ $rhs = $2;
+ }
+ }
+
+ # Process rule if we found one
+ if (defined $rule_number)
+ {
+
+ # deconstruct the RHS
+ $rhs =~ s|^/\* empty \*/$||;
+ my @rhs = split '\s', $rhs;
+ print "Rule $rule_number: $cur_nonterminal := @rhs\n" if $debug;
+
+ # We complain if the nonterminal appears as the last RHS element
+ # but not elsewhere, since "expr := expr + expr" is reasonable
+ my $lastrhs = pop @rhs;
+ if ( defined $lastrhs
+ && $cur_nonterminal eq $lastrhs
+ && !grep { $cur_nonterminal eq $_ } @rhs)
+ {
+ print
+ "Right recursion in rule $rule_number: $cur_nonterminal := $rhs\n";
+ }
+ }
+}
+
+exit 0;
diff --git a/src/tools/ci/README b/src/tools/ci/README
new file mode 100644
index 0000000..6c1cac8
--- /dev/null
+++ b/src/tools/ci/README
@@ -0,0 +1,67 @@
+Postgres Continuous Integration (CI)
+====================================
+
+Postgres has two forms of CI:
+
+1) All supported branches in the main postgres repository are continuously
+ tested via the buildfarm. As this covers only the main repository, it
+ cannot be used during development of features.
+
+ For details see https://buildfarm.postgresql.org/
+
+2) For not yet merged development work, CI can be enabled for some git hosting
+ providers. This allows developers to test patches on a number of platforms
+ before they are merged (or even submitted).
+
+
+Configuring CI on personal repositories
+=======================================
+
+Currently postgres contains CI support utilizing cirrus-ci. cirrus-ci
+currently is only available for github.
+
+
+Enabling cirrus-ci in a github repository
+=========================================
+
+To enable cirrus-ci on a repository, go to
+https://github.com/marketplace/cirrus-ci and select "Public
+Repositories". Then "Install it for free" and "Complete order". The next page
+allows to configure which repositories cirrus-ci has access to. Choose the
+relevant repository and "Install".
+
+See also https://cirrus-ci.org/guide/quick-start/
+
+Once enabled on a repository, future commits and pull-requests in that
+repository will automatically trigger CI builds. These are visible from the
+commit history / PRs, and can also be viewed in the cirrus-ci UI at
+https://cirrus-ci.com/github/<username>/<reponame>/
+
+Hint: all build log files are uploaded to cirrus-ci and can be downloaded
+from the "Artifacts" section fron the cirrus-ci UI after clicking into a
+specific task on a build's summary page.
+
+
+Images used for CI
+==================
+
+To keep CI times tolerable, most platforms use pre-generated images. Some
+platforms use containers, others use full VMs. Images for both are generated
+separately from CI runs, otherwise each git repository that is being tested
+would need to build its own set of containers, which would be wasteful (both
+in space and time.
+
+These images are built, on a daily basis, from the specifications in
+github.com/anarazel/pg-vm-images/
+
+
+Controlling CI via commit messages
+==================================
+
+The behavior of CI can be controlled by special content in commit
+messages. Currently the following controls are available:
+
+- ci-os-only: {(freebsd|linux|macos|windows)}
+
+ Only runs CI on operating systems specified. This can be useful when
+ addressing portability issues affecting only a subset of platforms.
diff --git a/src/tools/ci/cores_backtrace.sh b/src/tools/ci/cores_backtrace.sh
new file mode 100755
index 0000000..28d3cec
--- /dev/null
+++ b/src/tools/ci/cores_backtrace.sh
@@ -0,0 +1,50 @@
+#! /bin/sh
+
+if [ $# -ne 2 ]; then
+ echo "cores_backtrace.sh <os> <directory>"
+ exit 1
+fi
+
+os=$1
+directory=$2
+
+case $os in
+ freebsd|linux|macos)
+ ;;
+ *)
+ echo "unsupported operating system ${os}"
+ exit 1
+ ;;
+esac
+
+first=1
+for corefile in $(find "$directory" -type f) ; do
+ if [ "$first" -eq 1 ]; then
+ first=0
+ else
+ # to make it easier to separate the different crash reports
+ echo -e '\n\n'
+ fi
+
+ if [ "$os" = 'macos' ]; then
+ lldb -c $corefile --batch -o 'thread backtrace all' -o 'quit'
+ else
+ auxv=$(gdb --quiet --core ${corefile} --batch -ex 'info auxv' 2>/dev/null)
+ if [ $? -ne 0 ]; then
+ echo "could not process ${corefile}"
+ continue
+ fi
+
+ if [ "$os" = 'freebsd' ]; then
+ binary=$(echo "$auxv" | grep AT_EXECPATH | perl -pe "s/^.*\"(.*)\"\$/\$1/g")
+ elif [ "$os" = 'linux' ]; then
+ binary=$(echo "$auxv" | grep AT_EXECFN | perl -pe "s/^.*\"(.*)\"\$/\$1/g")
+ else
+ echo 'should not get here'
+ exit 1
+ fi
+
+ echo "dumping ${corefile} for ${binary}"
+ gdb --batch --quiet -ex "thread apply all bt full" -ex "quit" "$binary" "$corefile" 2>/dev/null
+ fi
+done
diff --git a/src/tools/ci/gcp_freebsd_repartition.sh b/src/tools/ci/gcp_freebsd_repartition.sh
new file mode 100755
index 0000000..2d5e173
--- /dev/null
+++ b/src/tools/ci/gcp_freebsd_repartition.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+set -e
+set -x
+
+# The default filesystem on freebsd gcp images is very slow to run tests on,
+# due to its 32KB block size
+#
+# XXX: It'd probably better to fix this in the image, using something like
+# https://people.freebsd.org/~lidl/blog/re-root.html
+
+# fix backup partition table after resize
+gpart recover da0
+gpart show da0
+# kill swap, so we can delete a partition
+swapoff -a || true
+# (apparently we can only have 4!?)
+gpart delete -i 3 da0
+gpart add -t freebsd-ufs -l data8k -a 4096 da0
+gpart show da0
+newfs -U -b 8192 /dev/da0p3
+
+# Migrate working directory
+du -hs $CIRRUS_WORKING_DIR
+mv $CIRRUS_WORKING_DIR $CIRRUS_WORKING_DIR.orig
+mkdir $CIRRUS_WORKING_DIR
+mount -o noatime /dev/da0p3 $CIRRUS_WORKING_DIR
+cp -r $CIRRUS_WORKING_DIR.orig/* $CIRRUS_WORKING_DIR/
diff --git a/src/tools/ci/pg_ci_base.conf b/src/tools/ci/pg_ci_base.conf
new file mode 100644
index 0000000..d8faa9c
--- /dev/null
+++ b/src/tools/ci/pg_ci_base.conf
@@ -0,0 +1,14 @@
+# Tends to produce too many core files, taking a long time
+restart_after_crash = false
+
+# So that tests using the "manually" started postgres on windows can use
+# prepared statements
+max_prepared_transactions = 10
+
+# Settings that make logs more useful
+log_autovacuum_min_duration = 0
+log_checkpoints = true
+log_connections = true
+log_disconnections = true
+log_line_prefix = '%m [%p][%b] %q[%a][%v:%x] '
+log_lock_waits = true
diff --git a/src/tools/ci/windows_build_config.pl b/src/tools/ci/windows_build_config.pl
new file mode 100644
index 0000000..59268a0
--- /dev/null
+++ b/src/tools/ci/windows_build_config.pl
@@ -0,0 +1,13 @@
+use strict;
+use warnings;
+
+our $config;
+
+$config->{"tap_tests"} = 1;
+$config->{"asserts"} = 1;
+
+$config->{"openssl"} = "c:/openssl/1.1/";
+$config->{"perl"} = "c:/strawberry/$ENV{DEFAULT_PERL_VERSION}/perl/";
+$config->{"python"} = "c:/python/";
+
+1;
diff --git a/src/tools/codelines b/src/tools/codelines
new file mode 100755
index 0000000..11e86ac
--- /dev/null
+++ b/src/tools/codelines
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# src/tools/codelines
+
+# This script is used to compute the total number of "C" lines in the release
+# This should be run from the top of the Git tree after a 'make distclean'
+find . -name '*.[chyl]' | xargs cat| wc -l
diff --git a/src/tools/copyright.pl b/src/tools/copyright.pl
new file mode 100755
index 0000000..182177e
--- /dev/null
+++ b/src/tools/copyright.pl
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+#################################################################
+# copyright.pl -- update copyright notices throughout the source tree, idempotently.
+#
+# Copyright (c) 2011-2022, PostgreSQL Global Development Group
+#
+# src/tools/copyright.pl
+#
+# FYI, Tie adds a trailing newline on the last line if missing.
+#################################################################
+
+use strict;
+use warnings;
+
+use File::Find;
+use File::Basename;
+use Tie::File;
+
+my $pgdg = 'PostgreSQL Global Development Group';
+my $cc = 'Copyright \(c\)';
+my $ccliteral = 'Copyright (c)';
+
+# year-1900 is what localtime(time) puts in element 5
+my $year = 1900 + ${ [ localtime(time) ] }[5];
+
+print "Using current year: $year\n";
+
+find({ wanted => \&wanted, no_chdir => 1 }, '.');
+
+sub wanted
+{
+
+ # prevent corruption of git indexes by ignoring any .git/
+ if (basename($_) eq '.git')
+ {
+ $File::Find::prune = 1;
+ return;
+ }
+
+ return if !-f $File::Find::name || -l $File::Find::name;
+
+ # skip file names with binary extensions
+ # How are these updated? bjm 2012-01-02
+ return if ($_ =~ m/\.(ico|bin|po|key)$/);
+
+ my @lines;
+ tie @lines, "Tie::File", $File::Find::name;
+
+ # We process all lines because some files have copyright
+ # strings embedded in them, e.g. src/bin/psql/help.c
+ foreach my $line (@lines)
+ {
+
+ # We only care about lines with a copyright notice.
+ next unless $line =~ m/$cc.*$pgdg/i;
+
+ # Skip line if it already matches the current year; if not
+ # we get $year-$year, e.g. 2012-2012.
+ next if $line =~ m/$cc $year, $pgdg/i;
+
+ # Skip already-updated lines too, to avoid unnecessary
+ # file updates.
+ next if $line =~ m/$cc \d{4}-$year, $pgdg/i;
+
+ # Apply the update, relying on Tie::File to write the file.
+ $line =~ s/$cc (\d{4})-\d{4}, $pgdg/$ccliteral $1-$year, $pgdg/i;
+ $line =~ s/$cc (\d{4}), $pgdg/$ccliteral $1-$year, $pgdg/i;
+ }
+ untie @lines;
+ return;
+}
+
+print "Manually update:\n";
+print " ./doc/src/sgml/legal.sgml in head and back branches\n";
+print " ./COPYRIGHT in back branches\n";
diff --git a/src/tools/editors/emacs.samples b/src/tools/editors/emacs.samples
new file mode 100644
index 0000000..529c98a
--- /dev/null
+++ b/src/tools/editors/emacs.samples
@@ -0,0 +1,91 @@
+;; -*- mode: emacs-lisp -*-
+
+;; This file contains code to set up Emacs to edit PostgreSQL source
+;; code. Copy these snippets into your .emacs file or equivalent, or
+;; use load-file to load this file directly.
+;;
+;; Note also that there is a .dir-locals.el file at the top of the
+;; PostgreSQL source tree, which contains many of the settings shown
+;; here (but not all, mainly because not all settings are allowed as
+;; local variables). So for light editing, you might not need any
+;; additional Emacs configuration.
+
+
+;;; C files
+
+;; Style that matches the formatting used by
+;; src/tools/pgindent/pgindent. Many extension projects also use this
+;; style.
+(c-add-style "postgresql"
+ '("bsd"
+ (c-auto-align-backslashes . nil)
+ (c-basic-offset . 4)
+ (c-offsets-alist . ((case-label . +)
+ (label . -)
+ (statement-case-open . +)))
+ (fill-column . 78)
+ (indent-tabs-mode . t)
+ (tab-width . 4)))
+
+(add-hook 'c-mode-hook
+ (defun postgresql-c-mode-hook ()
+ (when (string-match "/postgres\\(ql\\)?/" buffer-file-name)
+ (c-set-style "postgresql")
+ ;; Don't override the style we just set with the style in
+ ;; `dir-locals-file'. Emacs 23.4.1 needs this; it is obsolete,
+ ;; albeit harmless, by Emacs 24.3.1.
+ (set (make-local-variable 'ignored-local-variables)
+ (append '(c-file-style) ignored-local-variables)))))
+
+
+;;; Perl files
+
+;; Style that matches the formatting used by
+;; src/tools/pgindent/perltidyrc.
+(defun pgsql-perl-style ()
+ "Perl style adjusted for PostgreSQL project"
+ (interactive)
+ (setq perl-brace-imaginary-offset 0)
+ (setq perl-brace-offset 0)
+ (setq perl-continued-statement-offset 2)
+ (setq perl-continued-brace-offset (- perl-continued-statement-offset))
+ (setq perl-indent-level 4)
+ (setq perl-label-offset -2)
+ ;; Next two aren't marked safe-local-variable, so .dir-locals.el omits them.
+ (setq perl-indent-continued-arguments 4)
+ (setq perl-indent-parens-as-block t)
+ (setq indent-tabs-mode t)
+ (setq tab-width 4))
+
+(add-hook 'perl-mode-hook
+ (defun postgresql-perl-mode-hook ()
+ (when (string-match "/postgres\\(ql\\)?/" buffer-file-name)
+ (pgsql-perl-style))))
+
+
+;;; documentation files
+
+;; *.sgml files are actually XML
+(add-to-list 'auto-mode-alist '("/postgres\\(ql\\)?/.*\\.sgml\\'" . nxml-mode))
+
+(add-hook 'nxml-mode-hook
+ (defun postgresql-xml-mode-hook ()
+ (when (string-match "/postgres\\(ql\\)?/" buffer-file-name)
+ (setq fill-column 78)
+ (setq indent-tabs-mode nil))))
+
+;; The *.xsl files use 2-space indent, which is consistent with
+;; docbook-xsl sources and also the nxml-mode default. But the *.sgml
+;; files use 1-space indent, mostly for historical reasons at this
+;; point.
+(add-hook 'nxml-mode-hook
+ (defun postgresql-xml-src-mode-hook ()
+ (when (string-match "/postgres\\(ql\\)?/.*\\.sgml\\'" buffer-file-name)
+ (setq nxml-child-indent 1))))
+
+
+;;; Makefiles
+
+;; use GNU make mode instead of plain make mode
+(add-to-list 'auto-mode-alist '("/postgres\\(ql\\)?/.*Makefile.*" . makefile-gmake-mode))
+(add-to-list 'auto-mode-alist '("/postgres\\(ql\\)?/.*\\.mk\\'" . makefile-gmake-mode))
diff --git a/src/tools/editors/vim.samples b/src/tools/editors/vim.samples
new file mode 100644
index 0000000..ccbc93f
--- /dev/null
+++ b/src/tools/editors/vim.samples
@@ -0,0 +1,17 @@
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+"
+" These settings are appropriate for editing PostgreSQL code with vim
+"
+" You would copy this into your .vimrc or equivalent
+"
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+:if match(getcwd(), "/pgsql") >=0 || match(getcwd(), "/postgresql") >= 0
+
+: set cinoptions=(0
+: set tabstop=4
+: set shiftwidth=4
+
+:endif
+
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
diff --git a/src/tools/find_badmacros b/src/tools/find_badmacros
new file mode 100755
index 0000000..58f4308
--- /dev/null
+++ b/src/tools/find_badmacros
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# This script attempts to find bad ifdef's, i.e. ifdef's that use braces
+# but not the do { ... } while (0) syntax
+#
+# src/tools/find_badmacros
+#
+# This is useful for running before pgindent
+
+for FILE
+do
+ awk ' BEGIN {was_define = "N"}
+ { if (was_define == "Y" &&
+ $0 ~ /^{/)
+ printf "%s %d\n", FILENAME, NR
+ if ($0 ~ /^#define/)
+ was_define = "Y"
+ else
+ was_define = "N"
+ }' "$FILE"
+ grep -on '^#define.*{' "$FILE" | grep -v 'do[ ]*{'
+done
diff --git a/src/tools/find_static b/src/tools/find_static
new file mode 100755
index 0000000..1cc9ec3
--- /dev/null
+++ b/src/tools/find_static
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+# src/tools/find_static
+
+trap "rm -f /tmp/$$" 0 1 2 3 15
+
+# This script finds functions that are either never called, or
+# should be static.
+# Some functions, like library functions and debug_print functions,
+# should remain unchanged.
+
+# Run on a compiled source tree, from the top of the source tree
+
+# My nm utility has 9 characters of address which I strip, then a 'type'
+# character, with T as a text function, and U as an undefined function
+# symbol, then the function name.
+
+find . -name '[a-z]*.o' -type f -print | while read FILE
+do nm $FILE | cut -c17-100 |awk '{printf "%s\t%s\t%s\n", "'"$FILE"'",$1,$2}'
+done >/tmp/$$
+dropdb debug
+createdb debug
+echo "
+ create table debug (file text, scope char, func text);
+
+ copy debug from '/tmp/"$$"';
+
+ select *
+ into table debug2
+ from debug;
+
+ create index idebug on debug(scope,func);
+ create index idebug2 on debug2(func,scope);
+ vacuum debug;
+ vacuum debug2;
+
+ update debug2
+ set scope = '_'
+ from debug
+ where debug2.func = debug.func and
+ debug2.scope = 'T' and debug.scope = 'U';
+
+ delete from debug2
+ where scope = '_';
+
+ select *
+ from debug2
+ where scope = 'T' and func != 'main'
+ order by file, func;
+" |psql -X debug
diff --git a/src/tools/find_typedef b/src/tools/find_typedef
new file mode 100755
index 0000000..24e9b76
--- /dev/null
+++ b/src/tools/find_typedef
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+# src/tools/find_typedef
+
+# This script attempts to find all typedef's in the postgres binaries
+# by using 'objdump' or local equivalent to print typedef debugging symbols.
+# We need this because pgindent needs a list of typedef names.
+#
+# For this program to work, you must have compiled all code with
+# debugging symbols.
+#
+# We intentionally examine all files in the targeted directories so as to
+# find both .o files and executables. Therefore, ignore error messages about
+# unsuitable files being fed to objdump.
+#
+# This is known to work on Linux and on some BSDen, including macOS.
+#
+# Caution: on the platforms we use, this only prints typedefs that are used
+# to declare at least one variable or struct field. If you have say
+# "typedef struct foo { ... } foo;", and then the structure is only ever
+# referenced as "struct foo", "foo" will not be reported as a typedef,
+# causing pgindent to indent the typedef definition oddly. This is not a
+# huge problem, since by definition there's just the one misindented line.
+#
+# We get typedefs by reading "STABS":
+# http://www.informatik.uni-frankfurt.de/doc/texi/stabs_toc.html
+
+
+if [ "$#" -eq 0 -o ! -d "$1" ]
+then echo "Usage: $0 postgres_binary_directory [...]" 1>&2
+ exit 1
+fi
+
+for DIR
+do # if objdump -W is recognized, only one line of error should appear
+ if [ `objdump -W 2>&1 | wc -l` -eq 1 ]
+ then # Linux
+ objdump -W "$DIR"/* |
+ egrep -A3 '\(DW_TAG_typedef\)' |
+ awk ' $2 == "DW_AT_name" {print $NF}'
+ elif [ `readelf -w 2>&1 | wc -l` -gt 1 ]
+ then # FreeBSD, similar output to Linux
+ readelf -w "$DIR"/* |
+ egrep -A3 '\(DW_TAG_typedef\)' |
+ awk ' $1 == "DW_AT_name" {print $NF}'
+ fi
+done |
+grep -v ' ' | # some typedefs have spaces, remove them
+sort |
+uniq |
+# these are used both for typedefs and variable names
+# so do not include them
+egrep -v '^(date|interval|timestamp|ANY)$'
diff --git a/src/tools/fix-old-flex-code.pl b/src/tools/fix-old-flex-code.pl
new file mode 100644
index 0000000..8059cb5
--- /dev/null
+++ b/src/tools/fix-old-flex-code.pl
@@ -0,0 +1,66 @@
+#!/usr/bin/perl
+#----------------------------------------------------------------------
+#
+# fix-old-flex-code.pl
+#
+# flex versions before 2.5.36, with certain option combinations, produce
+# code that causes an "unused variable" warning. That's annoying, so
+# let's suppress it by inserting a dummy reference to the variable.
+# (That's exactly what 2.5.36 and later do ...)
+#
+# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/tools/fix-old-flex-code.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+# Get command line argument.
+usage() if $#ARGV != 0;
+my $filename = shift;
+
+# Suck in the whole file.
+local $/ = undef;
+my $cfile;
+open($cfile, '<', $filename) || die "opening $filename for reading: $!";
+my $ccode = <$cfile>;
+close($cfile);
+
+# No need to do anything if it's not flex 2.5.x for x < 36.
+exit 0 if $ccode !~ m/^#define YY_FLEX_MAJOR_VERSION 2$/m;
+exit 0 if $ccode !~ m/^#define YY_FLEX_MINOR_VERSION 5$/m;
+exit 0 if $ccode !~ m/^#define YY_FLEX_SUBMINOR_VERSION (\d+)$/m;
+exit 0 if $1 >= 36;
+
+# Apply the desired patch.
+$ccode =~
+ s|(struct yyguts_t \* yyg = \(struct yyguts_t\*\)yyscanner; /\* This var may be unused depending upon options. \*/
+.*?)
+ return yy_is_jam \? 0 : yy_current_state;
+|$1
+ (void) yyg;
+ return yy_is_jam ? 0 : yy_current_state;
+|s;
+
+# Write the modified file back out.
+open($cfile, '>', $filename) || die "opening $filename for writing: $!";
+print $cfile $ccode;
+close($cfile);
+
+exit 0;
+
+
+sub usage
+{
+ die <<EOM;
+Usage: fix-old-flex-code.pl c-file-name
+
+fix-old-flex-code.pl modifies a flex output file to suppress
+an unused-variable warning that occurs with older flex versions.
+
+Report bugs to <pgsql-bugs\@lists.postgresql.org>.
+EOM
+}
diff --git a/src/tools/gen_keywordlist.pl b/src/tools/gen_keywordlist.pl
new file mode 100644
index 0000000..88ea78f
--- /dev/null
+++ b/src/tools/gen_keywordlist.pl
@@ -0,0 +1,197 @@
+#----------------------------------------------------------------------
+#
+# gen_keywordlist.pl
+# Perl script that transforms a list of keywords into a ScanKeywordList
+# data structure that can be passed to ScanKeywordLookup().
+#
+# The input is a C header file containing a series of macro calls
+# PG_KEYWORD("keyword", ...)
+# Lines not starting with PG_KEYWORD are ignored. The keywords are
+# implicitly numbered 0..N-1 in order of appearance in the header file.
+# Currently, the keywords are required to appear in ASCII order.
+#
+# The output is a C header file that defines a "const ScanKeywordList"
+# variable named according to the -v switch ("ScanKeywords" by default).
+# The variable is marked "static" unless the -e switch is given.
+#
+# ScanKeywordList uses hash-based lookup, so this script also selects
+# a minimal perfect hash function for the keyword set, and emits a
+# static hash function that is referenced in the ScanKeywordList struct.
+# The hash function is case-insensitive unless --no-case-fold is specified.
+# Note that case folding works correctly only for all-ASCII keywords!
+#
+#
+# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/tools/gen_keywordlist.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use PerfectHash;
+
+my $output_path = '';
+my $extern = 0;
+my $case_fold = 1;
+my $varname = 'ScanKeywords';
+
+GetOptions(
+ 'output:s' => \$output_path,
+ 'extern' => \$extern,
+ 'case-fold!' => \$case_fold,
+ 'varname:s' => \$varname) || usage();
+
+my $kw_input_file = shift @ARGV || die "No input file.\n";
+
+# Make sure output_path ends in a slash if needed.
+if ($output_path ne '' && substr($output_path, -1) ne '/')
+{
+ $output_path .= '/';
+}
+
+$kw_input_file =~ /(\w+)\.h$/
+ || die "Input file must be named something.h.\n";
+my $base_filename = $1 . '_d';
+my $kw_def_file = $output_path . $base_filename . '.h';
+
+open(my $kif, '<', $kw_input_file) || die "$kw_input_file: $!\n";
+open(my $kwdef, '>', $kw_def_file) || die "$kw_def_file: $!\n";
+
+# Opening boilerplate for keyword definition header.
+printf $kwdef <<EOM, $base_filename, uc $base_filename, uc $base_filename;
+/*-------------------------------------------------------------------------
+ *
+ * %s.h
+ * List of keywords represented as a ScanKeywordList.
+ *
+ * Portions Copyright (c) 1996-2022, 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 src/tools/gen_keywordlist.pl
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef %s_H
+#define %s_H
+
+#include "common/kwlookup.h"
+
+EOM
+
+# Parse input file for keyword names.
+my @keywords;
+while (<$kif>)
+{
+ if (/^PG_KEYWORD\("(\w+)"/)
+ {
+ push @keywords, $1;
+ }
+}
+
+# When being case-insensitive, insist that the input be all-lower-case.
+if ($case_fold)
+{
+ foreach my $kw (@keywords)
+ {
+ die qq|The keyword "$kw" is not lower-case in $kw_input_file\n|
+ if ($kw ne lc $kw);
+ }
+}
+
+# Error out if the keyword names are not in ASCII order.
+#
+# While this isn't really necessary with hash-based lookup, it's still
+# helpful because it provides a cheap way to reject duplicate keywords.
+# Also, insisting on sorted order ensures that code that scans the keyword
+# table linearly will see the keywords in a canonical order.
+for my $i (0 .. $#keywords - 1)
+{
+ die
+ qq|The keyword "$keywords[$i + 1]" is out of order in $kw_input_file\n|
+ if ($keywords[$i] cmp $keywords[ $i + 1 ]) >= 0;
+}
+
+# Emit the string containing all the keywords.
+
+printf $kwdef qq|static const char %s_kw_string[] =\n\t"|, $varname;
+print $kwdef join qq|\\0"\n\t"|, @keywords;
+print $kwdef qq|";\n\n|;
+
+# Emit an array of numerical offsets which will be used to index into the
+# keyword string. Also determine max keyword length.
+
+printf $kwdef "static const uint16 %s_kw_offsets[] = {\n", $varname;
+
+my $offset = 0;
+my $max_len = 0;
+foreach my $name (@keywords)
+{
+ my $this_length = length($name);
+
+ print $kwdef "\t$offset,\n";
+
+ # Calculate the cumulative offset of the next keyword,
+ # taking into account the null terminator.
+ $offset += $this_length + 1;
+
+ # Update max keyword length.
+ $max_len = $this_length if $max_len < $this_length;
+}
+
+print $kwdef "};\n\n";
+
+# Emit a macro defining the number of keywords.
+# (In some places it's useful to have access to that as a constant.)
+
+printf $kwdef "#define %s_NUM_KEYWORDS %d\n\n", uc $varname, scalar @keywords;
+
+# Emit the definition of the hash function.
+
+my $funcname = $varname . "_hash_func";
+
+my $f = PerfectHash::generate_hash_function(\@keywords, $funcname,
+ case_fold => $case_fold);
+
+printf $kwdef qq|static %s\n|, $f;
+
+# Emit the struct that wraps all this lookup info into one variable.
+
+printf $kwdef "static " if !$extern;
+printf $kwdef "const ScanKeywordList %s = {\n", $varname;
+printf $kwdef qq|\t%s_kw_string,\n|, $varname;
+printf $kwdef qq|\t%s_kw_offsets,\n|, $varname;
+printf $kwdef qq|\t%s,\n|, $funcname;
+printf $kwdef qq|\t%s_NUM_KEYWORDS,\n|, uc $varname;
+printf $kwdef qq|\t%d\n|, $max_len;
+printf $kwdef "};\n\n";
+
+printf $kwdef "#endif\t\t\t\t\t\t\t/* %s_H */\n", uc $base_filename;
+
+
+sub usage
+{
+ die <<EOM;
+Usage: gen_keywordlist.pl [--output/-o <path>] [--varname/-v <varname>] [--extern/-e] [--[no-]case-fold] input_file
+ --output Output directory (default '.')
+ --varname Name for ScanKeywordList variable (default 'ScanKeywords')
+ --extern Allow the ScanKeywordList variable to be globally visible
+ --no-case-fold Keyword matching is to be case-sensitive
+
+gen_keywordlist.pl transforms a list of keywords into a ScanKeywordList.
+The output filename is derived from the input file by inserting _d,
+for example kwlist_d.h is produced from kwlist.h.
+EOM
+}
diff --git a/src/tools/git-external-diff b/src/tools/git-external-diff
new file mode 100644
index 0000000..39ddd01
--- /dev/null
+++ b/src/tools/git-external-diff
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# This script is used to produce git context diffs
+
+# Supplied parameters:
+# $1 $2 $3 $4 $5 $6 $7
+# path old-file old-hash old-mode new-file new-hash new-mode
+# 'path' is the git-tree-relative path of the file being diff'ed
+
+=comment
+
+This info is copied from the old wiki page on Working with git:
+
+Context diffs with Git
+
+Copy git-external-diff into libexec/git-core/ of your git installation
+and configure git to use that wrapper with:
+
+ git config [--global] diff.external git-external-diff
+
+--global makes the configuration global for your user - otherwise it is
+just configured for the current repository.
+
+For every command which displays diffs in some way you can use the
+parameter "--[no-]-ext-diff" to enable respectively disable using the
+external diff command.
+
+For the git diff command --ext-diff is enabled by default - for any
+other command like git log -p or git format-patch it is not!
+
+This method should work on all platforms supported by git.
+
+If you do not want to configure the external wrapper permanently or you
+want to overwrite it you can also invoke git like:
+
+ export GIT_EXTERNAL_DIFF=git-external-diff
+ git diff --[no-]ext-diff
+
+Alternatively, configure a git alias in ~/.gitconfig or .git/config:
+
+ [alias]
+ cdiff = !GIT_EXTERNAL_DIFF=git-context-diff git diff
+=cut
+
+old_hash="$3"
+new_hash=$(git hash-object "$5")
+
+# no change?
+[ "$old_hash" = "$new_hash" ] && exit 0
+
+[ "$DIFF_OPTS" = "" ] && DIFF_OPTS='-pcd'
+
+echo "diff --git a/$1 b/$1"
+echo "new file mode $7"
+echo "index ${old_hash:0:7}..${new_hash:0:7}"
+
+diff --label a/"$1" --label b/"$1" $DIFF_OPTS "$2" "$5"
+
+exit 0
diff --git a/src/tools/git_changelog b/src/tools/git_changelog
new file mode 100755
index 0000000..189cfc8
--- /dev/null
+++ b/src/tools/git_changelog
@@ -0,0 +1,419 @@
+#!/usr/bin/perl
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+#
+# src/tools/git_changelog
+#
+# Display all commits on active branches, merging together commits from
+# different branches that occur close together in time and with identical
+# log messages.
+#
+# By default, commits are annotated with branch and release info thus:
+# Branch: REL8_3_STABLE Release: REL8_3_2 [92c3a8004] 2008-03-29 00:15:37 +0000
+# This shows that the commit on REL8_3_STABLE was released in 8.3.2.
+# Commits on master will usually instead have notes like
+# Branch: master Release: REL8_4_BR [6fc9d4272] 2008-03-29 00:15:28 +0000
+# showing that this commit is ancestral to release branches 8.4 and later.
+# If no Release: marker appears, the commit hasn't yet made it into any
+# release.
+#
+# The --brief option shortens that to a format like:
+# YYYY-MM-DD [hash] abbreviated commit subject line
+# Since the branch isn't shown, this is mainly useful in conjunction
+# with --master-only.
+#
+# Most of the time, matchable commits occur in the same order on all branches,
+# and we print them out in that order. However, if commit A occurs before
+# commit B on branch X and commit B occurs before commit A on branch Y, then
+# there's no ordering which is consistent with both branches. In such cases
+# we sort a merged commit according to its timestamp on the newest branch
+# it appears in.
+#
+# The default output of this script is meant for generating minor release
+# notes, where we need to know which branches a merged commit affects.
+#
+# To generate major release notes, use:
+# git_changelog --master-only --brief --oldest-first --since='start-date'
+# To find the appropriate start date, use:
+# git show --summary $(git merge-base REL_12_STABLE master)
+# where the branch to mention is the previously forked-off branch. This
+# shows the last commit before that branch was made.
+#
+# Note that --master-only is an imperfect filter, since it will not detect
+# cases where a master patch was back-patched awhile later or with a slightly
+# different commit message. To find such cases, it's a good idea to look
+# through the output of
+# git_changelog --non-master-only --oldest-first --since='start-date'
+# and then remove anything from the --master-only output that would be
+# duplicative.
+
+
+use strict;
+use warnings;
+require Time::Local;
+require Getopt::Long;
+require IPC::Open2;
+
+# Adjust this list when the set of interesting branches changes.
+# (We could get this from "git branches", but not worth the trouble.)
+# NB: master must be first!
+my @BRANCHES = qw(master
+ REL_14_STABLE REL_13_STABLE
+ REL_12_STABLE REL_11_STABLE REL_10_STABLE REL9_6_STABLE REL9_5_STABLE
+ REL9_4_STABLE REL9_3_STABLE REL9_2_STABLE REL9_1_STABLE REL9_0_STABLE
+ REL8_4_STABLE REL8_3_STABLE REL8_2_STABLE REL8_1_STABLE REL8_0_STABLE
+ REL7_4_STABLE REL7_3_STABLE REL7_2_STABLE REL7_1_STABLE REL7_0_PATCHES
+ REL6_5_PATCHES REL6_4);
+
+# Might want to make this parameter user-settable.
+my $timestamp_slop = 24 * 60 * 60;
+
+my $brief = 0;
+my $details_after = 0;
+my $post_date = 0;
+my $master_only = 0;
+my $non_master_only = 0;
+my $oldest_first = 0;
+my $since;
+my @output_buffer;
+my $output_line = '';
+
+Getopt::Long::GetOptions(
+ 'brief' => \$brief,
+ 'details-after' => \$details_after,
+ 'master-only' => \$master_only,
+ 'non-master-only' => \$non_master_only,
+ 'post-date' => \$post_date,
+ 'oldest-first' => \$oldest_first,
+ 'since=s' => \$since) || usage();
+usage() if @ARGV;
+
+my @git = qw(git log --format=fuller --date=iso);
+push @git, '--since=' . $since if defined $since;
+
+# Collect the release tag data
+my %rel_tags;
+
+{
+ my $cmd = "git for-each-ref refs/tags";
+ my $pid = IPC::Open2::open2(my $git_out, my $git_in, $cmd)
+ || die "can't run $cmd: $!";
+ while (my $line = <$git_out>)
+ {
+ if ($line =~ m|^([a-f0-9]+)\s+commit\s+refs/tags/(\S+)|)
+ {
+ my $commit = $1;
+ my $tag = $2;
+ if ( $tag =~ /^REL_\d+_\d+$/
+ || $tag =~ /^REL\d+_\d+$/
+ || $tag =~ /^REL\d+_\d+_\d+$/)
+ {
+ $rel_tags{$commit} = $tag;
+ }
+ }
+ }
+ waitpid($pid, 0);
+ my $child_exit_status = $? >> 8;
+ die "$cmd failed" if $child_exit_status != 0;
+}
+
+# Collect the commit data
+my %all_commits;
+my %all_commits_by_branch;
+
+# This remembers where each branch sprouted from master. Note the values
+# will be wrong if --since terminates the log listing before the branch
+# sprouts; but in that case it doesn't matter since we also won't reach
+# the part of master where it would matter.
+my %sprout_tags;
+
+for my $branch (@BRANCHES)
+{
+ my @cmd = @git;
+ if ($branch eq "master")
+ {
+ push @cmd, "origin/$branch";
+ }
+ else
+ {
+ push @cmd, "--parents";
+ push @cmd, "master..origin/$branch";
+ }
+ my $pid = IPC::Open2::open2(my $git_out, my $git_in, @cmd)
+ || die "can't run @cmd: $!";
+ my $last_tag = undef;
+ my $last_parent;
+ my %commit;
+ while (my $line = <$git_out>)
+ {
+ if ($line =~ /^commit\s+(\S+)/)
+ {
+ push_commit(\%commit) if %commit;
+ $last_tag = $rel_tags{$1} if defined $rel_tags{$1};
+ %commit = (
+ 'branch' => $branch,
+ 'commit' => $1,
+ 'last_tag' => $last_tag,
+ 'message' => '',);
+ if ($line =~ /^commit\s+\S+\s+(\S+)/)
+ {
+ $last_parent = $1;
+ }
+ else
+ {
+ $last_parent = undef;
+ }
+ }
+ elsif ($line =~ /^Author:\s+(.*)/)
+ {
+ $commit{'author'} = $1;
+ }
+ elsif ($line =~ /^CommitDate:\s+(.*)/)
+ {
+ $commit{'date'} = $1;
+ }
+ elsif ($line =~ /^\s\s/)
+ {
+ $commit{'message'} .= $line;
+ }
+ }
+ push_commit(\%commit) if %commit;
+ $sprout_tags{$last_parent} = $branch if defined $last_parent;
+ waitpid($pid, 0);
+ my $child_exit_status = $? >> 8;
+ die "@cmd failed" if $child_exit_status != 0;
+}
+
+# Run through the master branch and apply tags. We already tagged the other
+# branches, but master needs a separate pass after we've acquired the
+# sprout_tags data. Also, in post-date mode we need to add phony entries
+# for branches that sprouted after a particular master commit was made.
+{
+ my $last_tag = undef;
+ my %sprouted_branches;
+ for my $cc (@{ $all_commits_by_branch{'master'} })
+ {
+ my $commit = $cc->{'commit'};
+ my $c = $cc->{'commits'}->[0];
+ $last_tag = $rel_tags{$commit} if defined $rel_tags{$commit};
+ if (defined $sprout_tags{$commit})
+ {
+ $last_tag = $sprout_tags{$commit};
+
+ # normalize branch names for making sprout tags
+ $last_tag =~ s/^(REL_\d+).*/$1_BR/;
+ $last_tag =~ s/^(REL\d+_\d+).*/$1_BR/;
+ }
+ $c->{'last_tag'} = $last_tag;
+ if ($post_date)
+ {
+ if (defined $sprout_tags{$commit})
+ {
+ $sprouted_branches{ $sprout_tags{$commit} } = 1;
+ }
+
+ # insert new commits between master and any other commits
+ my @new_commits = (shift @{ $cc->{'commits'} });
+ for my $branch (reverse sort keys %sprouted_branches)
+ {
+ my $ccopy = { %{$c} };
+ $ccopy->{'branch'} = $branch;
+ push @new_commits, $ccopy;
+ }
+ $cc->{'commits'} = [ @new_commits, @{ $cc->{'commits'} } ];
+ }
+ }
+}
+
+my %position;
+for my $branch (@BRANCHES)
+{
+ $position{$branch} = 0;
+}
+
+while (1)
+{
+ my $best_branch;
+ my $best_timestamp;
+ for my $branch (@BRANCHES)
+ {
+ my $leader = $all_commits_by_branch{$branch}->[ $position{$branch} ];
+ next if !defined $leader;
+ if (!defined $best_branch
+ || $leader->{'timestamp'} > $best_timestamp)
+ {
+ $best_branch = $branch;
+ $best_timestamp = $leader->{'timestamp'};
+ }
+ }
+ last if !defined $best_branch;
+ my $winner =
+ $all_commits_by_branch{$best_branch}->[ $position{$best_branch} ];
+
+ my $print_it = 1;
+ if ($master_only)
+ {
+ $print_it = (@{ $winner->{'commits'} } == 1)
+ && ($winner->{'commits'}[0]->{'branch'} eq 'master');
+ }
+ elsif ($non_master_only)
+ {
+ foreach my $c (@{ $winner->{'commits'} })
+ {
+ $print_it = 0 if ($c->{'branch'} eq 'master');
+ }
+ }
+
+ if ($print_it)
+ {
+ output_details($winner) if (!$details_after);
+ output_str("%s", $winner->{'message'} . "\n");
+ output_details($winner) if ($details_after);
+ unshift(@output_buffer, $output_line) if ($oldest_first);
+ $output_line = '';
+ }
+
+ $winner->{'done'} = 1;
+ for my $branch (@BRANCHES)
+ {
+ my $leader = $all_commits_by_branch{$branch}->[ $position{$branch} ];
+ if (defined $leader && $leader->{'done'})
+ {
+ ++$position{$branch};
+ redo;
+ }
+ }
+}
+
+print @output_buffer if ($oldest_first);
+
+sub push_commit
+{
+ my ($c) = @_;
+ my $ht = hash_commit($c);
+ my $ts = parse_datetime($c->{'date'});
+ my $cc;
+
+ # Note that this code will never merge two commits on the same branch,
+ # even if they have the same hash (author/message) and nearby
+ # timestamps. This means that there could be multiple potential
+ # matches when we come to add a commit from another branch. Prefer
+ # the closest-in-time one.
+ for my $candidate (@{ $all_commits{$ht} })
+ {
+ my $diff = abs($ts - $candidate->{'timestamp'});
+ if ($diff < $timestamp_slop
+ && !exists $candidate->{'branch_position'}{ $c->{'branch'} })
+ {
+ if (!defined $cc
+ || $diff < abs($ts - $cc->{'timestamp'}))
+ {
+ $cc = $candidate;
+ }
+ }
+ }
+ if (!defined $cc)
+ {
+ $cc = {
+ 'author' => $c->{'author'},
+ 'message' => $c->{'message'},
+ 'commit' => $c->{'commit'},
+ 'commits' => [],
+ 'timestamp' => $ts
+ };
+ push @{ $all_commits{$ht} }, $cc;
+ }
+
+ # stash only the fields we'll need later
+ my $smallc = {
+ 'branch' => $c->{'branch'},
+ 'commit' => $c->{'commit'},
+ 'date' => $c->{'date'},
+ 'last_tag' => $c->{'last_tag'}
+ };
+ push @{ $cc->{'commits'} }, $smallc;
+ push @{ $all_commits_by_branch{ $c->{'branch'} } }, $cc;
+ $cc->{'branch_position'}{ $c->{'branch'} } =
+ -1 + @{ $all_commits_by_branch{ $c->{'branch'} } };
+ return;
+}
+
+sub hash_commit
+{
+ my ($c) = @_;
+ return $c->{'author'} . "\0" . $c->{'message'};
+}
+
+sub parse_datetime
+{
+ my ($dt) = @_;
+ $dt =~
+ /^(\d\d\d\d)-(\d\d)-(\d\d)\s+(\d\d):(\d\d):(\d\d)\s+([-+])(\d\d)(\d\d)$/;
+ my $gm = Time::Local::timegm($6, $5, $4, $3, $2 - 1, $1);
+ my $tzoffset = ($8 * 60 + $9) * 60;
+ $tzoffset = -$tzoffset if $7 eq '-';
+ return $gm - $tzoffset;
+}
+
+sub output_str
+{
+ ($oldest_first) ? ($output_line .= sprintf(shift, @_)) : printf(@_);
+ return;
+}
+
+sub output_details
+{
+ my $item = shift;
+
+ if ($details_after)
+ {
+ $item->{'author'} =~ m{^(.*?)\s*<[^>]*>$};
+
+ # output only author name, not email address
+ output_str("(%s)\n", $1);
+ }
+ else
+ {
+ output_str("Author: %s\n", $item->{'author'});
+ }
+ foreach my $c (@{ $item->{'commits'} })
+ {
+ if ($brief)
+ {
+ $item->{'message'} =~ m/^\s*(.*)/;
+
+ output_str(
+ "%s [%s] %s\n",
+ substr($c->{'date'}, 0, 10),
+ substr($c->{'commit'}, 0, 9),
+ substr($1, 0, 56));
+ }
+ else
+ {
+ output_str("Branch: %s ", $c->{'branch'})
+ if (!$master_only);
+ output_str("Release: %s ", $c->{'last_tag'})
+ if (defined $c->{'last_tag'});
+ output_str("[%s] %s\n", substr($c->{'commit'}, 0, 9),
+ $c->{'date'});
+ }
+ }
+ output_str("\n");
+ return;
+}
+
+sub usage
+{
+ print STDERR <<EOM;
+Usage: git_changelog [--brief/-b] [--details-after/-d] [--master-only/-m] [--non-master-only/-n] [--oldest-first/-o] [--post-date/-p] [--since=SINCE]
+ --brief Shorten commit descriptions, omitting branch identification
+ --details-after Show branch and author info after the commit description
+ --master-only Show only commits made just in the master branch
+ --non-master-only Show only commits made just in back branches
+ --oldest-first Show oldest commits first
+ --post-date Show branches made after a commit occurred
+ --since Show only commits dated since SINCE
+EOM
+ exit 1;
+}
diff --git a/src/tools/ifaddrs/.gitignore b/src/tools/ifaddrs/.gitignore
new file mode 100644
index 0000000..2e90032
--- /dev/null
+++ b/src/tools/ifaddrs/.gitignore
@@ -0,0 +1 @@
+/test_ifaddrs
diff --git a/src/tools/ifaddrs/Makefile b/src/tools/ifaddrs/Makefile
new file mode 100644
index 0000000..2abffaf
--- /dev/null
+++ b/src/tools/ifaddrs/Makefile
@@ -0,0 +1,28 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/tools/ifaddrs
+#
+# Copyright (c) 2003-2022, PostgreSQL Global Development Group
+#
+# src/tools/ifaddrs/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/tools/ifaddrs
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+libpq_backend_dir = $(top_builddir)/src/backend/libpq
+
+override CPPFLAGS := -I$(libpq_backend_dir) $(CPPFLAGS)
+
+OBJS = \
+ test_ifaddrs.o
+
+all: test_ifaddrs
+
+test_ifaddrs: test_ifaddrs.o $(libpq_backend_dir)/ifaddr.o
+ $(CC) $(CFLAGS) test_ifaddrs.o $(libpq_backend_dir)/ifaddr.o $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
+
+clean distclean maintainer-clean:
+ rm -f test_ifaddrs$(X) $(OBJS)
diff --git a/src/tools/ifaddrs/README b/src/tools/ifaddrs/README
new file mode 100644
index 0000000..9f92803
--- /dev/null
+++ b/src/tools/ifaddrs/README
@@ -0,0 +1,12 @@
+src/tools/ifaddrs/README
+
+test_ifaddrs
+============
+
+This program prints the addresses and netmasks of all the IPv4 and IPv6
+interfaces on the local machine. It is useful for testing that this
+functionality works on various platforms. If "samehost" and "samenet"
+in pg_hba.conf don't seem to work right, run this program to see what
+is happening.
+
+Usage: test_ifaddrs
diff --git a/src/tools/ifaddrs/test_ifaddrs.c b/src/tools/ifaddrs/test_ifaddrs.c
new file mode 100644
index 0000000..b8dbb84
--- /dev/null
+++ b/src/tools/ifaddrs/test_ifaddrs.c
@@ -0,0 +1,73 @@
+/*
+ * src/tools/ifaddrs/test_ifaddrs.c
+ *
+ *
+ * test_ifaddrs.c
+ * test pg_foreach_ifaddr()
+ */
+
+#include "postgres.h"
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "libpq/ifaddr.h"
+
+
+static void
+print_addr(struct sockaddr *addr)
+{
+ char buffer[256];
+ int ret,
+ len;
+
+ switch (addr->sa_family)
+ {
+ case AF_INET:
+ len = sizeof(struct sockaddr_in);
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ len = sizeof(struct sockaddr_in6);
+ break;
+#endif
+ default:
+ len = sizeof(struct sockaddr_storage);
+ break;
+ }
+
+ ret = getnameinfo(addr, len, buffer, sizeof(buffer), NULL, 0,
+ NI_NUMERICHOST);
+ if (ret != 0)
+ printf("[unknown: family %d]", addr->sa_family);
+ else
+ printf("%s", buffer);
+}
+
+static void
+callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
+{
+ printf("addr: ");
+ print_addr(addr);
+ printf(" mask: ");
+ print_addr(mask);
+ printf("\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+#ifdef WIN32
+ WSADATA wsaData;
+
+ if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
+ {
+ fprintf(stderr, "WSAStartup failed\n");
+ return 1;
+ }
+#endif
+
+ if (pg_foreach_ifaddr(callback, NULL) < 0)
+ fprintf(stderr, "pg_foreach_ifaddr failed: %s\n", strerror(errno));
+ return 0;
+}
diff --git a/src/tools/make_ctags b/src/tools/make_ctags
new file mode 100755
index 0000000..9e952ce
--- /dev/null
+++ b/src/tools/make_ctags
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+# src/tools/make_ctags
+
+command -v ctags >/dev/null || \
+ { echo "'ctags' program not found" 1>&2; exit 1; }
+
+trap "rm -f /tmp/$$" 0 1 2 3 15
+rm -f ./tags
+
+IS_EXUBERANT=""
+ctags --version 2>&1 | grep Exuberant && IS_EXUBERANT="Y"
+
+# List of kinds supported by Exuberant Ctags 5.8
+# generated by ctags --list-kinds
+# --c-kinds was called --c-types before 2003
+# c classes
+# d macro definitions
+# e enumerators (values inside an enumeration)
+# f function definitions
+# g enumeration names
+# l local variables [off]
+# m class, struct, and union members
+# n namespaces
+# p function prototypes [off]
+# s structure names
+# t typedefs
+# u union names
+# v variable definitions
+# x external and forward variable declarations [off]
+
+if [ "$IS_EXUBERANT" ]
+then FLAGS="--c-kinds=+dfmstuv"
+else FLAGS="-dt"
+fi
+
+# this is outputting the tags into the file 'tags', and appending
+find `pwd`/ -type f -name '*.[chyl]' -print |
+ xargs ctags -a -f tags "$FLAGS"
+
+# Sorting non-Exuberant ctags file allows for fast searching of the tags file.
+# Since etags file has a header that we cannot sort in with the other entries
+# we skip the sort step.
+if [ ! "$IS_EXUBERANT" -a ! "$EMACS_MODE" ]
+then LC_ALL=C
+ export LC_ALL
+ sort tags >/tmp/$$ && mv /tmp/$$ tags
+fi
+
+find . \( -name 'CVS' -prune \) -o \( -name .git -prune \) -o -type d -print |
+while read DIR
+do [ "$DIR" != "." ] && ln -f -s `echo "$DIR" | sed 's;/[^/]*;/..;g'`/tags "$DIR"/tags
+done
diff --git a/src/tools/make_etags b/src/tools/make_etags
new file mode 100755
index 0000000..9288ef7
--- /dev/null
+++ b/src/tools/make_etags
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# src/tools/make_etags
+
+command -v etags >/dev/null || \
+ { echo "'etags' program not found" 1>&2; exit 1; }
+
+rm -f ./TAGS
+
+find `pwd`/ -type f -name '*.[chyl]' -print |
+ xargs etags --append -o TAGS
+
+find . \( -name CVS -prune \) -o \( -name .git -prune \) -o -type d -print |
+while read DIR
+do [ "$DIR" != "." ] && ln -f -s `pwd`/TAGS "$DIR"
+done
diff --git a/src/tools/make_mkid b/src/tools/make_mkid
new file mode 100755
index 0000000..6f16061
--- /dev/null
+++ b/src/tools/make_mkid
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# src/tools/make_mkid
+
+mkid `find \`pwd\`/ \( -name _deadcode -a -prune \) -o \
+ -type f -name '*.[chyl]' -print|sed 's;//;/;g'`
+
+find . \( -name .git -a -prune \) -o -type d -print |while read DIR
+do
+ [ "$DIR" != "." ] && ln -f -s `echo "$DIR" | sed 's;/[^/]*;/..;g'`/ID $DIR/ID
+done
diff --git a/src/tools/mark_pgdllimport.pl b/src/tools/mark_pgdllimport.pl
new file mode 100755
index 0000000..7b51ae3
--- /dev/null
+++ b/src/tools/mark_pgdllimport.pl
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+
+#----------------------------------------------------------------------
+#
+# mark_pgdllimport.pl
+# Perl script that tries to add PGDLLIMPORT markings to PostgreSQL
+# header files.
+#
+# This relies on a few idiosyncracies of the PostgreSQL coding style,
+# such as the fact that we always use "extern" in function
+# declarations, and that we don't use // comments. It's not very
+# smart and may not catch all cases.
+#
+# It's probably a good idea to run pgindent on any files that this
+# script modifies before committing. This script uses as arguments
+# a list of the header files to scan for the markings.
+#
+# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/tools/mark_pgdllimport.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+for my $include_file (@ARGV)
+{
+ open(my $rfh, '<', $include_file) || die "$include_file: $!";
+ my $buffer = '';
+ my $num_pgdllimport_added = 0;
+
+ while (my $raw_line = <$rfh>)
+ {
+ my $needs_pgdllimport = 1;
+
+ # By convention we declare global variables explicitly extern. We're
+ # looking for those not already marked with PGDLLIMPORT.
+ $needs_pgdllimport = 0
+ if $raw_line !~ /^extern\s+/
+ || $raw_line =~ /PGDLLIMPORT/;
+
+ # Make a copy of the line and perform a simple-minded comment strip.
+ # Also strip trailing whitespace.
+ my $stripped_line = $raw_line;
+ $stripped_line =~ s/\/\*.*\*\///g;
+ $stripped_line =~ s/\s+$//;
+
+ # Variable declarations should end in a semicolon. If we see an
+ # opening parenthesis, it's probably a function declaration.
+ $needs_pgdllimport = 0
+ if $stripped_line !~ /;$/
+ || $stripped_line =~ /\(/;
+
+ # Add PGDLLIMPORT marker, if required.
+ if ($needs_pgdllimport)
+ {
+ $raw_line =~ s/^extern/extern PGDLLIMPORT/;
+ ++$num_pgdllimport_added;
+ }
+
+ # Add line to buffer.
+ $buffer .= $raw_line;
+ }
+
+ close($rfh);
+
+ # If we added any PGDLLIMPORT markers, rewrite the file.
+ if ($num_pgdllimport_added > 0)
+ {
+ printf "%s: adding %d PGDLLIMPORT markers\n",
+ $include_file, $num_pgdllimport_added;
+ open(my $wfh, '>', $include_file) || die "$include_file: $!";
+ print $wfh $buffer;
+ close($wfh);
+ }
+}
diff --git a/src/tools/msvc/.gitignore b/src/tools/msvc/.gitignore
new file mode 100644
index 0000000..2470e78
--- /dev/null
+++ b/src/tools/msvc/.gitignore
@@ -0,0 +1,3 @@
+# Custom configuration files for MSVC build
+/config.pl
+/buildenv.pl
diff --git a/src/tools/msvc/Install.pm b/src/tools/msvc/Install.pm
new file mode 100644
index 0000000..8de79c6
--- /dev/null
+++ b/src/tools/msvc/Install.pm
@@ -0,0 +1,779 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+package Install;
+
+#
+# Package that provides 'make install' functionality for msvc builds
+#
+# src/tools/msvc/Install.pm
+#
+use strict;
+use warnings;
+use Carp;
+use File::Basename;
+use File::Copy;
+use File::Find ();
+
+use Exporter;
+our (@ISA, @EXPORT_OK);
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(Install);
+
+my $insttype;
+my @client_contribs = ('oid2name', 'pgbench', 'vacuumlo');
+my @client_program_files = (
+ 'clusterdb', 'createdb', 'createuser', 'dropdb',
+ 'dropuser', 'ecpg', 'libecpg', 'libecpg_compat',
+ 'libpgtypes', 'libpq', 'pg_amcheck', 'pg_basebackup',
+ 'pg_config', 'pg_dump', 'pg_dumpall', 'pg_isready',
+ 'pg_receivewal', 'pg_recvlogical', 'pg_restore', 'psql',
+ 'reindexdb', 'vacuumdb', @client_contribs);
+
+sub lcopy
+{
+ my $src = shift;
+ my $target = shift;
+
+ if (-f $target)
+ {
+ unlink $target || confess "Could not delete $target\n";
+ }
+
+ (my $retval = copy($src, $target))
+ || confess "Could not copy $src to $target\n";
+
+ return $retval;
+}
+
+sub Install
+{
+ $| = 1;
+
+ my $target = shift;
+ $insttype = shift;
+ $insttype = "all" unless ($insttype);
+
+ # if called from vcregress, the config will be passed to us
+ # so no need to re-include these
+ our $config = shift;
+ unless ($config)
+ {
+
+ # suppress warning about harmless redeclaration of $config
+ no warnings 'misc';
+ do "./config_default.pl";
+ do "./config.pl" if (-f "config.pl");
+ }
+
+ # Move to the root path depending on the current location.
+ if (-f "../../../configure")
+ {
+ chdir("../../..");
+ }
+ elsif (-f "../../../../configure")
+ {
+ chdir("../../../..");
+ }
+
+ my $conf = "";
+ if (-d "debug")
+ {
+ $conf = "debug";
+ }
+ if (-d "release")
+ {
+ $conf = "release";
+ }
+ die "Could not find debug or release binaries" if ($conf eq "");
+ my $majorver = DetermineMajorVersion();
+ print "Installing version $majorver for $conf in $target\n";
+
+ my @client_dirs = ('bin', 'lib', 'share', 'symbols');
+ my @all_dirs = (
+ @client_dirs, 'doc', 'doc/contrib', 'doc/extension', 'share/contrib',
+ 'share/extension', 'share/timezonesets', 'share/tsearch_data');
+ if ($insttype eq "client")
+ {
+ EnsureDirectories($target, @client_dirs);
+ }
+ else
+ {
+ EnsureDirectories($target, @all_dirs);
+ }
+
+ CopySolutionOutput($conf, $target);
+ my $sample_files = [];
+ my @top_dir = ("src");
+ @top_dir = ("src\\bin", "src\\interfaces") if ($insttype eq "client");
+ File::Find::find(
+ {
+ wanted => sub {
+ /^.*\.sample\z/s
+ && push(@$sample_files, $File::Find::name);
+
+ # Don't find files of in-tree temporary installations.
+ $_ eq 'share' and $File::Find::prune = 1;
+ }
+ },
+ @top_dir);
+ CopySetOfFiles('config files', $sample_files, $target . '/share/');
+ CopyFiles(
+ 'Import libraries',
+ $target . '/lib/',
+ "$conf\\", "postgres\\postgres.lib", "libpgcommon\\libpgcommon.lib",
+ "libpgport\\libpgport.lib");
+ CopyContribFiles($config, $target);
+ CopyIncludeFiles($target);
+
+ if ($insttype ne "client")
+ {
+ CopySetOfFiles(
+ 'timezone names',
+ [ glob('src\timezone\tznames\*.txt') ],
+ $target . '/share/timezonesets/');
+ CopyFiles(
+ 'timezone sets',
+ $target . '/share/timezonesets/',
+ 'src/timezone/tznames/', 'Default', 'Australia', 'India');
+ CopySetOfFiles(
+ 'BKI files',
+ [ glob("src\\backend\\catalog\\postgres.*") ],
+ $target . '/share/');
+ CopySetOfFiles(
+ 'SQL files',
+ [ glob("src\\backend\\catalog\\*.sql") ],
+ $target . '/share/');
+ CopyFiles(
+ 'Information schema data', $target . '/share/',
+ 'src/backend/catalog/', 'sql_features.txt');
+ CopyFiles(
+ 'Error code data', $target . '/share/',
+ 'src/backend/utils/', 'errcodes.txt');
+ GenerateTimezoneFiles($target, $conf);
+ GenerateTsearchFiles($target);
+ CopySetOfFiles(
+ 'Stopword files',
+ [ glob("src\\backend\\snowball\\stopwords\\*.stop") ],
+ $target . '/share/tsearch_data/');
+ CopySetOfFiles(
+ 'Dictionaries sample files',
+ [ glob("src\\backend\\tsearch\\dicts\\*_sample*") ],
+ $target . '/share/tsearch_data/');
+
+ my $pl_extension_files = [];
+ my @pldirs = ('src/pl/plpgsql/src');
+ push @pldirs, "src/pl/plperl" if $config->{perl};
+ push @pldirs, "src/pl/plpython" if $config->{python};
+ push @pldirs, "src/pl/tcl" if $config->{tcl};
+ File::Find::find(
+ {
+ wanted => sub {
+ /^(.*--.*\.sql|.*\.control)\z/s
+ && push(@$pl_extension_files, $File::Find::name);
+
+ # Don't find files of in-tree temporary installations.
+ $_ eq 'share' and $File::Find::prune = 1;
+ }
+ },
+ @pldirs);
+ CopySetOfFiles('PL Extension files',
+ $pl_extension_files, $target . '/share/extension/');
+ }
+
+ GenerateNLSFiles($target, $config->{nls}, $majorver) if ($config->{nls});
+
+ print "Installation complete.\n";
+ return;
+}
+
+sub EnsureDirectories
+{
+ my $target = shift;
+ mkdir $target unless -d ($target);
+ while (my $d = shift)
+ {
+ mkdir $target . '/' . $d unless -d ($target . '/' . $d);
+ }
+ return;
+}
+
+sub CopyFiles
+{
+ my $what = shift;
+ my $target = shift;
+ my $basedir = shift;
+
+ print "Copying $what";
+ while (my $f = shift)
+ {
+ print ".";
+ $f = $basedir . $f;
+ die "No file $f\n" if (!-f $f);
+ lcopy($f, $target . basename($f)) || croak "Could not copy $f: $!\n";
+ }
+ print "\n";
+ return;
+}
+
+sub CopySetOfFiles
+{
+ my $what = shift;
+ my $flist = shift;
+ my $target = shift;
+ print "Copying $what" if $what;
+ foreach (@$flist)
+ {
+ my $tgt = $target . basename($_);
+ print ".";
+ lcopy($_, $tgt) || croak "Could not copy $_: $!\n";
+ }
+ print "\n";
+ return;
+}
+
+sub CopySolutionOutput
+{
+ my $conf = shift;
+ my $target = shift;
+ my $rem =
+ qr{Project\("\{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942\}"\) = "([^"]+)"};
+
+ my $sln = read_file("pgsql.sln") || croak "Could not open pgsql.sln\n";
+
+ my $vcproj = 'vcproj';
+ if ($sln =~
+ /Microsoft Visual Studio Solution File, Format Version (\d+)\.\d+/
+ && $1 >= 11)
+ {
+ $vcproj = 'vcxproj';
+ }
+
+ print "Copying build output files...";
+ while ($sln =~ $rem)
+ {
+ my $pf = $1;
+
+ # Hash-of-arrays listing where to install things. For each
+ # subdirectory there's a hash key, and the value is an array
+ # of file extensions to install in that subdirectory. Example:
+ # { 'bin' => [ 'dll', 'lib' ],
+ # 'lib' => [ 'lib' ] }
+ my %install_list;
+ my $is_sharedlib = 0;
+
+ $sln =~ s/$rem//;
+
+ next
+ if ($insttype eq "client" && !grep { $_ eq $pf }
+ @client_program_files);
+
+ my $proj = read_file("$pf.$vcproj")
+ || croak "Could not open $pf.$vcproj\n";
+
+ # Check if this project uses a shared library by looking if
+ # SO_MAJOR_VERSION is defined in its Makefile, whose path
+ # can be found using the resource file of this project.
+ if (( $vcproj eq 'vcxproj'
+ && $proj =~ qr{ResourceCompile\s*Include="([^"]+)"})
+ || ( $vcproj eq 'vcproj'
+ && $proj =~ qr{File\s*RelativePath="([^\"]+)\.rc"}))
+ {
+ my $projpath = dirname($1);
+ my $mfname =
+ -e "$projpath/GNUmakefile"
+ ? "$projpath/GNUmakefile"
+ : "$projpath/Makefile";
+ my $mf = read_file($mfname) || croak "Could not open $mfname\n";
+
+ $is_sharedlib = 1 if ($mf =~ /^SO_MAJOR_VERSION\s*=\s*(.*)$/mg);
+ }
+
+ if ($vcproj eq 'vcproj' && $proj =~ qr{ConfigurationType="([^"]+)"})
+ {
+ if ($1 == 1)
+ {
+ push(@{ $install_list{'bin'} }, "exe");
+ }
+ elsif ($1 == 2)
+ {
+ push(@{ $install_list{'lib'} }, "dll");
+ if ($is_sharedlib)
+ {
+ push(@{ $install_list{'bin'} }, "dll");
+ push(@{ $install_list{'lib'} }, "lib");
+ }
+ }
+ else
+ {
+
+ # Static libraries, such as libpgport, only used internally
+ # during build, don't install.
+ next;
+ }
+ }
+ elsif ($vcproj eq 'vcxproj'
+ && $proj =~ qr{<ConfigurationType>(\w+)</ConfigurationType>})
+ {
+ if ($1 eq 'Application')
+ {
+ push(@{ $install_list{'bin'} }, "exe");
+ }
+ elsif ($1 eq 'DynamicLibrary')
+ {
+ push(@{ $install_list{'lib'} }, "dll");
+ if ($is_sharedlib)
+ {
+ push(@{ $install_list{'bin'} }, "dll");
+ push(@{ $install_list{'lib'} }, "lib");
+ }
+ }
+ else # 'StaticLibrary'
+ {
+
+ # Static lib, such as libpgport, only used internally
+ # during build, don't install.
+ next;
+ }
+ }
+ else
+ {
+ croak "Could not parse $pf.$vcproj\n";
+ }
+
+ # Install each element
+ foreach my $dir (keys %install_list)
+ {
+ foreach my $ext (@{ $install_list{$dir} })
+ {
+ lcopy("$conf\\$pf\\$pf.$ext", "$target\\$dir\\$pf.$ext")
+ || croak "Could not copy $pf.$ext\n";
+ }
+ }
+ lcopy("$conf\\$pf\\$pf.pdb", "$target\\symbols\\$pf.pdb")
+ || croak "Could not copy $pf.pdb\n";
+ print ".";
+ }
+ print "\n";
+ return;
+}
+
+sub GenerateTimezoneFiles
+{
+ my $target = shift;
+ my $conf = shift;
+ my $mf = read_file("src/timezone/Makefile");
+ $mf =~ s{\\\r?\n}{}g;
+
+ $mf =~ /^TZDATAFILES\s*:?=\s*(.*)$/m
+ || die "Could not find TZDATAFILES line in timezone makefile\n";
+ my @tzfiles = split /\s+/, $1;
+
+ print "Generating timezone files...";
+
+ my @args = ("$conf/zic/zic", '-d', "$target/share/timezone");
+ foreach (@tzfiles)
+ {
+ my $tzfile = $_;
+ $tzfile =~ s|\$\(srcdir\)|src/timezone|;
+ push(@args, $tzfile);
+ }
+
+ system(@args);
+ print "\n";
+ return;
+}
+
+sub GenerateTsearchFiles
+{
+ my $target = shift;
+
+ print "Generating tsearch script...";
+ my $F;
+ my $tmpl = read_file('src/backend/snowball/snowball.sql.in');
+ my $mf = read_file('src/backend/snowball/Makefile');
+ $mf =~ s{\\\r?\n}{}g;
+ $mf =~ /^LANGUAGES\s*=\s*(.*)$/m
+ || die "Could not find LANGUAGES line in snowball Makefile\n";
+ my @pieces = split /\s+/, $1;
+ open($F, '>', "$target/share/snowball_create.sql")
+ || die "Could not write snowball_create.sql";
+ print $F read_file('src/backend/snowball/snowball_func.sql.in');
+
+ while ($#pieces > 0)
+ {
+ my $lang = shift @pieces || last;
+ my $asclang = shift @pieces || last;
+ my $txt = $tmpl;
+ my $stop = '';
+
+ if (-s "src/backend/snowball/stopwords/$lang.stop")
+ {
+ $stop = ", StopWords=$lang";
+ }
+
+ $txt =~ s#_LANGNAME_#${lang}#gs;
+ $txt =~ s#_DICTNAME_#${lang}_stem#gs;
+ $txt =~ s#_CFGNAME_#${lang}#gs;
+ $txt =~ s#_ASCDICTNAME_#${asclang}_stem#gs;
+ $txt =~ s#_NONASCDICTNAME_#${lang}_stem#gs;
+ $txt =~ s#_STOPWORDS_#$stop#gs;
+ print $F $txt;
+ print ".";
+ }
+ close($F);
+ print "\n";
+ return;
+}
+
+sub CopyContribFiles
+{
+ my $config = shift;
+ my $target = shift;
+
+ print "Copying contrib data files...";
+ foreach my $subdir ('contrib', 'src/test/modules')
+ {
+ my $D;
+ opendir($D, $subdir) || croak "Could not opendir on $subdir!\n";
+ while (my $d = readdir($D))
+ {
+ # These configuration-based exclusions must match vcregress.pl
+ next if ($d eq "uuid-ossp" && !defined($config->{uuid}));
+ next if ($d eq "sslinfo" && !defined($config->{openssl}));
+ next if ($d eq "pgcrypto" && !defined($config->{openssl}));
+ next if ($d eq "xml2" && !defined($config->{xml}));
+ next if ($d =~ /_plperl$/ && !defined($config->{perl}));
+ next if ($d =~ /_plpython$/ && !defined($config->{python}));
+ next if ($d eq "sepgsql");
+
+ CopySubdirFiles($subdir, $d, $config, $target);
+ }
+ }
+ print "\n";
+ return;
+}
+
+sub CopySubdirFiles
+{
+ my $subdir = shift;
+ my $module = shift;
+ my $config = shift;
+ my $target = shift;
+
+ return if ($module =~ /^\./);
+ return unless (-f "$subdir/$module/Makefile");
+ return
+ if ($insttype eq "client" && !grep { $_ eq $module } @client_contribs);
+
+ my $mf = read_file("$subdir/$module/Makefile");
+ $mf =~ s{\\\r?\n}{}g;
+
+ # Note: we currently don't support setting MODULEDIR in the makefile
+ my $moduledir = 'contrib';
+
+ my $flist = '';
+ if ($mf =~ /^EXTENSION\s*=\s*(.*)$/m) { $flist .= $1 }
+ if ($flist ne '')
+ {
+ $moduledir = 'extension';
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ foreach my $f (split /\s+/, $flist)
+ {
+ lcopy("$subdir/$module/$f.control",
+ "$target/share/extension/$f.control")
+ || croak("Could not copy file $f.control in contrib $module");
+ print '.';
+ }
+ }
+
+ $flist = '';
+ if ($mf =~ /^DATA_built\s*=\s*(.*)$/m) { $flist .= $1 }
+ if ($mf =~ /^DATA\s*=\s*(.*)$/m) { $flist .= " $1" }
+ $flist =~ s/^\s*//; # Remove leading spaces if we had only DATA_built
+
+ if ($flist ne '')
+ {
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ foreach my $f (split /\s+/, $flist)
+ {
+ lcopy("$subdir/$module/$f",
+ "$target/share/$moduledir/" . basename($f))
+ || croak("Could not copy file $f in contrib $module");
+ print '.';
+ }
+ }
+
+ $flist = '';
+ if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) { $flist .= $1 }
+ if ($flist ne '')
+ {
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ foreach my $f (split /\s+/, $flist)
+ {
+ lcopy("$subdir/$module/$f",
+ "$target/share/tsearch_data/" . basename($f))
+ || croak("Could not copy file $f in $subdir $module");
+ print '.';
+ }
+ }
+
+ {
+ $flist = '';
+ if ($mf =~ /^HEADERS\s*=\s*(.*)$/m) { $flist .= $1 }
+ my @modlist = ();
+ my %fmodlist = ();
+ while ($mf =~ /^HEADERS_([^\s=]+)\s*=\s*(.*)$/mg)
+ {
+ $fmodlist{$1} .= $2;
+ }
+
+ if ($mf =~ /^MODULE_big\s*=\s*(.*)$/m)
+ {
+ push @modlist, $1;
+ if ($flist ne '')
+ {
+ $fmodlist{$1} = $flist;
+ $flist = '';
+ }
+ }
+ elsif ($mf =~ /^MODULES\s*=\s*(.*)$/m)
+ {
+ push @modlist, split /\s+/, $1;
+ }
+
+ croak "HEADERS requires MODULE_big in $subdir $module"
+ if $flist ne '';
+
+ foreach my $mod (keys %fmodlist)
+ {
+ croak "HEADERS_$mod for unknown module in $subdir $module"
+ unless grep { $_ eq $mod } @modlist;
+ $flist = ParseAndCleanRule($fmodlist{$mod}, $mf);
+ EnsureDirectories($target, "include", "include/server",
+ "include/server/$moduledir",
+ "include/server/$moduledir/$mod");
+ foreach my $f (split /\s+/, $flist)
+ {
+ lcopy("$subdir/$module/$f",
+ "$target/include/server/$moduledir/$mod/" . basename($f))
+ || croak("Could not copy file $f in $subdir $module");
+ print '.';
+ }
+ }
+ }
+
+ $flist = '';
+ if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) { $flist .= $1 }
+ if ($flist ne '')
+ {
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ # Special case for contrib/spi
+ $flist =
+ "autoinc.example insert_username.example moddatetime.example refint.example"
+ if ($module eq 'spi');
+ foreach my $f (split /\s+/, $flist)
+ {
+ lcopy("$subdir/$module/$f", "$target/doc/$moduledir/$f")
+ || croak("Could not copy file $f in contrib $module");
+ print '.';
+ }
+ }
+ return;
+}
+
+sub ParseAndCleanRule
+{
+ my $flist = shift;
+ my $mf = shift;
+
+ # Strip out $(addsuffix) rules
+ if (index($flist, '$(addsuffix ') >= 0)
+ {
+ my $pcount = 0;
+ my $i;
+ for (
+ $i = index($flist, '$(addsuffix ') + 12;
+ $i < length($flist);
+ $i++)
+ {
+ $pcount++ if (substr($flist, $i, 1) eq '(');
+ $pcount-- if (substr($flist, $i, 1) eq ')');
+ last if ($pcount < 0);
+ }
+ $flist =
+ substr($flist, 0, index($flist, '$(addsuffix '))
+ . substr($flist, $i + 1);
+ }
+ return $flist;
+}
+
+sub CopyIncludeFiles
+{
+ my $target = shift;
+
+ EnsureDirectories($target, 'include', 'include/libpq', 'include/internal',
+ 'include/internal/libpq', 'include/server', 'include/server/parser');
+
+ CopyFiles(
+ 'Public headers', $target . '/include/',
+ 'src/include/', 'postgres_ext.h',
+ 'pg_config.h', 'pg_config_ext.h',
+ 'pg_config_os.h', 'pg_config_manual.h');
+ lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/')
+ || croak 'Could not copy libpq-fs.h';
+
+ CopyFiles(
+ 'Libpq headers',
+ $target . '/include/',
+ 'src/interfaces/libpq/', 'libpq-fe.h', 'libpq-events.h');
+ CopyFiles(
+ 'Libpq internal headers',
+ $target . '/include/internal/',
+ 'src/interfaces/libpq/', 'libpq-int.h', 'fe-auth-sasl.h',
+ 'pqexpbuffer.h');
+
+ CopyFiles(
+ 'Internal headers',
+ $target . '/include/internal/',
+ 'src/include/', 'c.h', 'port.h', 'postgres_fe.h');
+ lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/')
+ || croak 'Could not copy pqcomm.h';
+
+ CopyFiles(
+ 'Server headers',
+ $target . '/include/server/',
+ 'src/include/', 'pg_config.h', 'pg_config_ext.h', 'pg_config_os.h');
+ CopyFiles(
+ 'Grammar header',
+ $target . '/include/server/parser/',
+ 'src/backend/parser/', 'gram.h');
+ CopySetOfFiles(
+ '',
+ [ glob("src\\include\\*.h") ],
+ $target . '/include/server/');
+ my $D;
+ opendir($D, 'src/include') || croak "Could not opendir on src/include!\n";
+
+ CopyFiles(
+ 'PL/pgSQL header',
+ $target . '/include/server/',
+ 'src/pl/plpgsql/src/', 'plpgsql.h');
+
+ # some xcopy progs don't like mixed slash style paths
+ (my $ctarget = $target) =~ s!/!\\!g;
+ while (my $d = readdir($D))
+ {
+ next if ($d =~ /^\./);
+ next if ($d eq '.git');
+ next if ($d eq 'CVS');
+ next unless (-d "src/include/$d");
+
+ EnsureDirectories("$target/include/server/$d");
+ my @args = (
+ 'xcopy', '/s', '/i', '/q', '/r', '/y', "src\\include\\$d\\*.h",
+ "$ctarget\\include\\server\\$d\\");
+ system(@args) && croak("Failed to copy include directory $d\n");
+ }
+ closedir($D);
+
+ my $mf = read_file('src/interfaces/ecpg/include/Makefile');
+ $mf =~ s{\\\r?\n}{}g;
+ $mf =~ /^ecpg_headers\s*=\s*(.*)$/m
+ || croak "Could not find ecpg_headers line\n";
+ CopyFiles(
+ 'ECPG headers',
+ $target . '/include/',
+ 'src/interfaces/ecpg/include/',
+ 'ecpg_config.h', split /\s+/, $1);
+ $mf =~ /^informix_headers\s*=\s*(.*)$/m
+ || croak "Could not find informix_headers line\n";
+ EnsureDirectories($target . '/include', 'informix', 'informix/esql');
+ CopyFiles(
+ 'ECPG informix headers',
+ $target . '/include/informix/esql/',
+ 'src/interfaces/ecpg/include/',
+ split /\s+/, $1);
+ return;
+}
+
+sub GenerateNLSFiles
+{
+ my $target = shift;
+ my $nlspath = shift;
+ my $majorver = shift;
+
+ print "Installing NLS files...";
+ EnsureDirectories($target, "share/locale");
+ my @flist;
+ File::Find::find(
+ {
+ wanted => sub {
+ /^nls\.mk\z/s
+ && !push(@flist, $File::Find::name);
+ }
+ },
+ "src");
+ foreach (@flist)
+ {
+ my $prgm = DetermineCatalogName($_);
+ s/nls.mk/po/;
+ my $dir = $_;
+ next unless ($dir =~ /([^\/]+)\/po$/);
+ foreach (glob("$dir/*.po"))
+ {
+ my $lang;
+ next unless /([^\/]+)\.po/;
+ $lang = $1;
+
+ EnsureDirectories($target, "share/locale/$lang",
+ "share/locale/$lang/LC_MESSAGES");
+ my @args = (
+ "$nlspath\\bin\\msgfmt",
+ '-o',
+ "$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm-$majorver.mo",
+ $_);
+ system(@args) && croak("Could not run msgfmt on $dir\\$_");
+ print ".";
+ }
+ }
+ print "\n";
+ return;
+}
+
+sub DetermineMajorVersion
+{
+ my $f = read_file('src/include/pg_config.h')
+ || croak 'Could not open pg_config.h';
+ $f =~ /^#define\s+PG_MAJORVERSION\s+"([^"]+)"/m
+ || croak 'Could not determine major version';
+ return $1;
+}
+
+sub DetermineCatalogName
+{
+ my $filename = shift;
+
+ my $f = read_file($filename) || croak "Could not open $filename";
+ $f =~ /CATALOG_NAME\s*\:?=\s*(\S+)/m
+ || croak "Could not determine catalog name in $filename";
+ return $1;
+}
+
+sub read_file
+{
+ my $filename = shift;
+ my $F;
+ local $/ = undef;
+ open($F, '<', $filename) || die "Could not open file $filename\n";
+ my $txt = <$F>;
+ close($F);
+
+ return $txt;
+}
+
+1;
diff --git a/src/tools/msvc/MSBuildProject.pm b/src/tools/msvc/MSBuildProject.pm
new file mode 100644
index 0000000..f24d9e5
--- /dev/null
+++ b/src/tools/msvc/MSBuildProject.pm
@@ -0,0 +1,533 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+package MSBuildProject;
+
+#
+# Package that encapsulates a MSBuild project file (Visual C++ 2013 or greater)
+#
+# src/tools/msvc/MSBuildProject.pm
+#
+
+use Carp;
+use strict;
+use warnings;
+use base qw(Project);
+
+no warnings qw(redefine); ## no critic
+
+sub _new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{filenameExtension} = '.vcxproj';
+ $self->{ToolsVersion} = '4.0';
+
+ return $self;
+}
+
+sub WriteHeader
+{
+ my ($self, $f) = @_;
+
+ print $f <<EOF;
+<?xml version="1.0" encoding="Windows-1252"?>
+<Project DefaultTargets="Build" ToolsVersion="$self->{ToolsVersion}" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+EOF
+ $self->WriteConfigurationHeader($f, 'Debug');
+ $self->WriteConfigurationHeader($f, 'Release');
+ print $f <<EOF;
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>$self->{guid}</ProjectGuid>
+EOF
+ # Check whether WindowsSDKVersion env variable is present.
+ # Add WindowsTargetPlatformVersion node if so.
+ my $sdkVersion = $ENV{'WindowsSDKVersion'};
+ if (defined($sdkVersion))
+ {
+ # remove trailing backslash if necessary.
+ $sdkVersion =~ s/\\$//;
+ print $f <<EOF
+ <WindowsTargetPlatformVersion>$sdkVersion</WindowsTargetPlatformVersion>
+EOF
+ }
+ print $f <<EOF;
+ </PropertyGroup>
+ <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.Default.props" />
+EOF
+ $self->WriteConfigurationPropertyGroup($f, 'Release',
+ { wholeopt => 'false' });
+ $self->WriteConfigurationPropertyGroup($f, 'Debug',
+ { wholeopt => 'false' });
+ print $f <<EOF;
+ <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+EOF
+ $self->WritePropertySheetsPropertyGroup($f, 'Release');
+ $self->WritePropertySheetsPropertyGroup($f, 'Debug');
+ print $f <<EOF;
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+EOF
+ $self->WriteAdditionalProperties($f, 'Debug');
+ $self->WriteAdditionalProperties($f, 'Release');
+ print $f <<EOF;
+ </PropertyGroup>
+EOF
+
+ $self->WriteItemDefinitionGroup(
+ $f, 'Debug',
+ {
+ defs => "_DEBUG;DEBUG=1",
+ opt => 'Disabled',
+ strpool => 'false',
+ runtime => 'MultiThreadedDebugDLL'
+ });
+ $self->WriteItemDefinitionGroup(
+ $f,
+ 'Release',
+ {
+ defs => "",
+ opt => 'Full',
+ strpool => 'true',
+ runtime => 'MultiThreadedDLL'
+ });
+ return;
+}
+
+sub AddDefine
+{
+ my ($self, $def) = @_;
+
+ $self->{defines} .= $def . ';';
+ return;
+}
+
+sub WriteReferences
+{
+ my ($self, $f) = @_;
+
+ my @references = @{ $self->{references} };
+
+ if (scalar(@references))
+ {
+ print $f <<EOF;
+ <ItemGroup>
+EOF
+ foreach my $ref (@references)
+ {
+ print $f <<EOF;
+ <ProjectReference Include="$ref->{name}$ref->{filenameExtension}">
+ <Project>$ref->{guid}</Project>
+ </ProjectReference>
+EOF
+ }
+ print $f <<EOF;
+ </ItemGroup>
+EOF
+ }
+ return;
+}
+
+sub WriteFiles
+{
+ my ($self, $f) = @_;
+ print $f <<EOF;
+ <ItemGroup>
+EOF
+ my @grammarFiles = ();
+ my @resourceFiles = ();
+ my %uniquefiles;
+ foreach my $fileNameWithPath (sort keys %{ $self->{files} })
+ {
+ confess "Bad format filename '$fileNameWithPath'\n"
+ unless ($fileNameWithPath =~ m!^(.*)/([^/]+)\.(c|cpp|y|l|rc)$!);
+ my $dir = $1;
+ my $fileName = $2;
+ if ($fileNameWithPath =~ /\.y$/ or $fileNameWithPath =~ /\.l$/)
+ {
+ push @grammarFiles, $fileNameWithPath;
+ }
+ elsif ($fileNameWithPath =~ /\.rc$/)
+ {
+ push @resourceFiles, $fileNameWithPath;
+ }
+ elsif (defined($uniquefiles{$fileName}))
+ {
+
+ # File already exists, so fake a new name
+ my $obj = $dir;
+ $obj =~ s!/!_!g;
+
+ print $f <<EOF;
+ <ClCompile Include="$fileNameWithPath">
+ <ObjectFileName Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">.\\debug\\$self->{name}\\${obj}_$fileName.obj</ObjectFileName>
+ <ObjectFileName Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">.\\release\\$self->{name}\\${obj}_$fileName.obj</ObjectFileName>
+ </ClCompile>
+EOF
+ }
+ else
+ {
+ $uniquefiles{$fileName} = 1;
+ print $f <<EOF;
+ <ClCompile Include="$fileNameWithPath" />
+EOF
+ }
+
+ }
+ print $f <<EOF;
+ </ItemGroup>
+EOF
+ if (scalar(@grammarFiles))
+ {
+ print $f <<EOF;
+ <ItemGroup>
+EOF
+ foreach my $grammarFile (@grammarFiles)
+ {
+ (my $outputFile = $grammarFile) =~ s/\.(y|l)$/.c/;
+ if ($grammarFile =~ /\.y$/)
+ {
+ $outputFile =~
+ s{^src\\pl\\plpgsql\\src\\gram.c$}{src\\pl\\plpgsql\\src\\pl_gram.c};
+ print $f <<EOF;
+ <CustomBuild Include="$grammarFile">
+ <Message Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">Running bison on $grammarFile</Message>
+ <Command Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">perl "src\\tools\\msvc\\pgbison.pl" "$grammarFile"</Command>
+ <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs>
+ <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">$outputFile;%(Outputs)</Outputs>
+ <Message Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">Running bison on $grammarFile</Message>
+ <Command Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">perl "src\\tools\\msvc\\pgbison.pl" "$grammarFile"</Command>
+ <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs>
+ <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">$outputFile;%(Outputs)</Outputs>
+ </CustomBuild>
+EOF
+ }
+ else #if ($grammarFile =~ /\.l$/)
+ {
+ print $f <<EOF;
+ <CustomBuild Include="$grammarFile">
+ <Message Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">Running flex on $grammarFile</Message>
+ <Command Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">perl "src\\tools\\msvc\\pgflex.pl" "$grammarFile"</Command>
+ <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs>
+ <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">$outputFile;%(Outputs)</Outputs>
+ <Message Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">Running flex on $grammarFile</Message>
+ <Command Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">perl "src\\tools\\msvc\\pgflex.pl" "$grammarFile"</Command>
+ <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs>
+ <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">$outputFile;%(Outputs)</Outputs>
+ </CustomBuild>
+EOF
+ }
+ }
+ print $f <<EOF;
+ </ItemGroup>
+EOF
+ }
+ if (scalar(@resourceFiles))
+ {
+ print $f <<EOF;
+ <ItemGroup>
+EOF
+ foreach my $rcFile (@resourceFiles)
+ {
+ print $f <<EOF;
+ <ResourceCompile Include="$rcFile" />
+EOF
+ }
+ print $f <<EOF;
+ </ItemGroup>
+EOF
+ }
+ return;
+}
+
+sub WriteConfigurationHeader
+{
+ my ($self, $f, $cfgname) = @_;
+ print $f <<EOF;
+ <ProjectConfiguration Include="$cfgname|$self->{platform}">
+ <Configuration>$cfgname</Configuration>
+ <Platform>$self->{platform}</Platform>
+ </ProjectConfiguration>
+EOF
+ return;
+}
+
+sub WriteConfigurationPropertyGroup
+{
+ my ($self, $f, $cfgname, $p) = @_;
+ my $cfgtype =
+ ($self->{type} eq "exe")
+ ? 'Application'
+ : ($self->{type} eq "dll" ? 'DynamicLibrary' : 'StaticLibrary');
+
+ print $f <<EOF;
+ <PropertyGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'" Label="Configuration">
+ <ConfigurationType>$cfgtype</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>$p->{wholeopt}</WholeProgramOptimization>
+ <PlatformToolset>$self->{PlatformToolset}</PlatformToolset>
+ </PropertyGroup>
+EOF
+ return;
+}
+
+sub WritePropertySheetsPropertyGroup
+{
+ my ($self, $f, $cfgname) = @_;
+ print $f <<EOF;
+ <ImportGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'" Label="PropertySheets">
+ <Import Project="\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props" Condition="exists('\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+EOF
+ return;
+}
+
+sub WriteAdditionalProperties
+{
+ my ($self, $f, $cfgname) = @_;
+ print $f <<EOF;
+ <OutDir Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">.\\$cfgname\\$self->{name}\\</OutDir>
+ <IntDir Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">.\\$cfgname\\$self->{name}\\</IntDir>
+ <LinkIncremental Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">false</LinkIncremental>
+EOF
+ return;
+}
+
+sub WriteItemDefinitionGroup
+{
+ my ($self, $f, $cfgname, $p) = @_;
+ my $cfgtype =
+ ($self->{type} eq "exe")
+ ? 'Application'
+ : ($self->{type} eq "dll" ? 'DynamicLibrary' : 'StaticLibrary');
+ my $libs = $self->GetAdditionalLinkerDependencies($cfgname, ';');
+
+ my $targetmachine =
+ $self->{platform} eq 'Win32' ? 'MachineX86' : 'MachineX64';
+
+ my $includes = join ';', @{ $self->{includes} }, "";
+
+ print $f <<EOF;
+ <ItemDefinitionGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">
+ <ClCompile>
+ <Optimization>$p->{opt}</Optimization>
+ <AdditionalIncludeDirectories>$self->{prefixincludes}src/include;src/include/port/win32;src/include/port/win32_msvc;$includes\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;__WINDOWS__;__WIN32__;WIN32_STACK_RLIMIT=4194304;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE$self->{defines}$p->{defs}\%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>$p->{strpool}</StringPooling>
+ <RuntimeLibrary>$p->{runtime}</RuntimeLibrary>
+ <DisableSpecificWarnings>$self->{disablewarnings};\%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <AdditionalOptions>/MP \%(AdditionalOptions)</AdditionalOptions>
+ <AssemblerOutput>
+ </AssemblerOutput>
+ <AssemblerListingLocation>.\\$cfgname\\$self->{name}\\</AssemblerListingLocation>
+ <ObjectFileName>.\\$cfgname\\$self->{name}\\</ObjectFileName>
+ <ProgramDataBaseFileName>.\\$cfgname\\$self->{name}\\</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ </ClCompile>
+ <Link>
+ <OutputFile>.\\$cfgname\\$self->{name}\\$self->{name}.$self->{type}</OutputFile>
+ <AdditionalDependencies>$libs;\%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>\%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>libc;\%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+ <StackReserveSize>4194304</StackReserveSize>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>.\\$cfgname\\$self->{name}\\$self->{name}.pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>.\\$cfgname\\$self->{name}\\$self->{name}.map</MapFileName>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <!-- Permit links to MinGW-built, 32-bit DLLs (default before VS2012). -->
+ <ImageHasSafeExceptionHandlers/>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>$targetmachine</TargetMachine>
+EOF
+ if ($self->{disablelinkerwarnings})
+ {
+ print $f
+ " <AdditionalOptions>/ignore:$self->{disablelinkerwarnings} \%(AdditionalOptions)</AdditionalOptions>\n";
+ }
+ if ($self->{implib})
+ {
+ my $l = $self->{implib};
+ $l =~ s/__CFGNAME__/$cfgname/g;
+ print $f " <ImportLibrary>$l</ImportLibrary>\n";
+ }
+ if ($self->{def})
+ {
+ my $d = $self->{def};
+ $d =~ s/__CFGNAME__/$cfgname/g;
+ print $f " <ModuleDefinitionFile>$d</ModuleDefinitionFile>\n";
+ }
+ print $f <<EOF;
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>src\\include;\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+EOF
+ if ($self->{builddef})
+ {
+ print $f <<EOF;
+ <PreLinkEvent>
+ <Message>Generate DEF file</Message>
+ <Command>perl src\\tools\\msvc\\gendef.pl $cfgname\\$self->{name} $self->{platform}</Command>
+ </PreLinkEvent>
+EOF
+ }
+ print $f <<EOF;
+ </ItemDefinitionGroup>
+EOF
+ return;
+}
+
+sub Footer
+{
+ my ($self, $f) = @_;
+ $self->WriteReferences($f);
+
+ print $f <<EOF;
+ <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
+EOF
+ return;
+}
+
+package VC2013Project;
+
+#
+# Package that encapsulates a Visual C++ 2013 project file
+#
+
+use strict;
+use warnings;
+use base qw(MSBuildProject);
+
+no warnings qw(redefine); ## no critic
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{vcver} = '12.00';
+ $self->{PlatformToolset} = 'v120';
+ $self->{ToolsVersion} = '12.0';
+
+ return $self;
+}
+
+package VC2015Project;
+
+#
+# Package that encapsulates a Visual C++ 2015 project file
+#
+
+use strict;
+use warnings;
+use base qw(MSBuildProject);
+
+no warnings qw(redefine); ## no critic
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{vcver} = '14.00';
+ $self->{PlatformToolset} = 'v140';
+ $self->{ToolsVersion} = '14.0';
+
+ return $self;
+}
+
+package VC2017Project;
+
+#
+# Package that encapsulates a Visual C++ 2017 project file
+#
+
+use strict;
+use warnings;
+use base qw(MSBuildProject);
+
+no warnings qw(redefine); ## no critic
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{vcver} = '15.00';
+ $self->{PlatformToolset} = 'v141';
+ $self->{ToolsVersion} = '15.0';
+
+ return $self;
+}
+
+package VC2019Project;
+
+#
+# Package that encapsulates a Visual C++ 2019 project file
+#
+
+use strict;
+use warnings;
+use base qw(MSBuildProject);
+
+no warnings qw(redefine); ## no critic
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{vcver} = '16.00';
+ $self->{PlatformToolset} = 'v142';
+ $self->{ToolsVersion} = '16.0';
+
+ return $self;
+}
+
+package VC2022Project;
+
+#
+# Package that encapsulates a Visual C++ 2022 project file
+#
+
+use strict;
+use warnings;
+use base qw(MSBuildProject);
+
+no warnings qw(redefine); ## no critic
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{vcver} = '17.00';
+ $self->{PlatformToolset} = 'v143';
+ $self->{ToolsVersion} = '17.0';
+
+ return $self;
+}
+
+1;
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
new file mode 100644
index 0000000..990c223
--- /dev/null
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -0,0 +1,1204 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+package Mkvcbuild;
+
+#
+# Package that generates build files for msvc build
+#
+# src/tools/msvc/Mkvcbuild.pm
+#
+use strict;
+use warnings;
+
+use Carp;
+use if ($^O eq "MSWin32"), 'Win32';
+use Project;
+use Solution;
+use Cwd;
+use File::Copy;
+use Config;
+use VSObjectFactory;
+use List::Util qw(first);
+
+use Exporter;
+our (@ISA, @EXPORT_OK);
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(Mkvcbuild);
+
+my $solution;
+my $libpgport;
+my $libpgcommon;
+my $libpgfeutils;
+my $postgres;
+my $libpq;
+my @unlink_on_exit;
+
+# Set of variables for modules in contrib/ and src/test/modules/
+my $contrib_defines = {};
+my @contrib_uselibpq = ();
+my @contrib_uselibpgport = ();
+my @contrib_uselibpgcommon = ();
+my $contrib_extralibs = { 'libpq_pipeline' => ['ws2_32.lib'] };
+my $contrib_extraincludes = {};
+my $contrib_extrasource = {};
+my @contrib_excludes = (
+ 'bool_plperl', 'commit_ts',
+ 'hstore_plperl', 'hstore_plpython',
+ 'intagg', 'jsonb_plperl',
+ 'jsonb_plpython', 'ltree_plpython',
+ 'sepgsql', 'brin',
+ 'test_extensions', 'test_misc',
+ 'test_pg_dump', 'snapshot_too_old',
+ 'unsafe_tests');
+
+# Set of variables for frontend modules
+my $frontend_defines = { 'initdb' => 'FRONTEND' };
+my @frontend_uselibpq =
+ ('pg_amcheck', 'pg_ctl', 'pg_upgrade', 'pgbench', 'psql', 'initdb');
+my @frontend_uselibpgport = (
+ 'pg_amcheck', 'pg_archivecleanup',
+ 'pg_test_fsync', 'pg_test_timing',
+ 'pg_upgrade', 'pg_waldump',
+ 'pgbench');
+my @frontend_uselibpgcommon = (
+ 'pg_amcheck', 'pg_archivecleanup',
+ 'pg_test_fsync', 'pg_test_timing',
+ 'pg_upgrade', 'pg_waldump',
+ 'pgbench');
+my $frontend_extralibs = {
+ 'initdb' => ['ws2_32.lib'],
+ 'pg_amcheck' => ['ws2_32.lib'],
+ 'pg_restore' => ['ws2_32.lib'],
+ 'pgbench' => ['ws2_32.lib'],
+ 'psql' => ['ws2_32.lib']
+};
+my $frontend_extraincludes = {
+ 'initdb' => ['src/timezone'],
+ 'psql' => ['src/backend']
+};
+my $frontend_extrasource = {
+ 'psql' => ['src/bin/psql/psqlscanslash.l'],
+ 'pgbench' =>
+ [ 'src/bin/pgbench/exprscan.l', 'src/bin/pgbench/exprparse.y' ]
+};
+my @frontend_excludes = (
+ 'pgevent', 'pg_basebackup', 'pg_rewind', 'pg_dump',
+ 'pg_waldump', 'scripts');
+
+sub mkvcbuild
+{
+ our $config = shift;
+
+ chdir('../../..') if (-d '../msvc' && -d '../../../src');
+ die 'Must run from root or msvc directory'
+ unless (-d 'src/tools/msvc' && -d 'src');
+
+ my $vsVersion = DetermineVisualStudioVersion();
+
+ $solution = CreateSolution($vsVersion, $config);
+
+ our @pgportfiles = qw(
+ chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+ getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+ snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
+ dirent.c dlopen.c getopt.c getopt_long.c link.c
+ pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c
+ pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
+ pqsignal.c mkdtemp.c qsort.c qsort_arg.c bsearch_arg.c quotes.c system.c
+ strerror.c tar.c
+ win32common.c win32env.c win32error.c win32fseek.c win32ntdll.c
+ win32security.c win32setlocale.c win32stat.c);
+
+ push(@pgportfiles, 'strtof.c') if ($vsVersion < '14.00');
+
+ if ($vsVersion >= '9.00')
+ {
+ push(@pgportfiles, 'pg_crc32c_sse42_choose.c');
+ push(@pgportfiles, 'pg_crc32c_sse42.c');
+ push(@pgportfiles, 'pg_crc32c_sb8.c');
+ }
+ else
+ {
+ push(@pgportfiles, 'pg_crc32c_sb8.c');
+ }
+
+ our @pgcommonallfiles = qw(
+ archive.c base64.c checksum_helper.c compression.c
+ config_info.c controldata_utils.c d2s.c encnames.c exec.c
+ f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c
+ keywords.c kwlookup.c link-canary.c md5_common.c
+ pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+ rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+ username.c wait_error.c wchar.c);
+
+ if ($solution->{options}->{openssl})
+ {
+ push(@pgcommonallfiles, 'cryptohash_openssl.c');
+ push(@pgcommonallfiles, 'hmac_openssl.c');
+ push(@pgcommonallfiles, 'protocol_openssl.c');
+ }
+ else
+ {
+ push(@pgcommonallfiles, 'cryptohash.c');
+ push(@pgcommonallfiles, 'hmac.c');
+ push(@pgcommonallfiles, 'md5.c');
+ push(@pgcommonallfiles, 'sha1.c');
+ push(@pgcommonallfiles, 'sha2.c');
+ }
+
+ our @pgcommonfrontendfiles = (
+ @pgcommonallfiles, qw(fe_memutils.c
+ logging.c restricted_token.c sprompt.c));
+
+ our @pgcommonbkndfiles = @pgcommonallfiles;
+
+ our @pgfeutilsfiles = qw(
+ archive.c cancel.c conditional.c connect_utils.c mbprint.c option_utils.c
+ parallel_slot.c print.c psqlscan.l psqlscan.c query_utils.c simple_list.c
+ string_utils.c recovery_gen.c);
+
+ $libpgport = $solution->AddProject('libpgport', 'lib', 'misc');
+ $libpgport->AddDefine('FRONTEND');
+ $libpgport->AddFiles('src/port', @pgportfiles);
+
+ $libpgcommon = $solution->AddProject('libpgcommon', 'lib', 'misc');
+ $libpgcommon->AddDefine('FRONTEND');
+ $libpgcommon->AddFiles('src/common', @pgcommonfrontendfiles);
+
+ $libpgfeutils = $solution->AddProject('libpgfeutils', 'lib', 'misc');
+ $libpgfeutils->AddDefine('FRONTEND');
+ $libpgfeutils->AddIncludeDir('src/interfaces/libpq');
+ $libpgfeutils->AddFiles('src/fe_utils', @pgfeutilsfiles);
+
+ $postgres = $solution->AddProject('postgres', 'exe', '', 'src/backend');
+ $postgres->AddIncludeDir('src/backend');
+ $postgres->AddDir('src/backend/port/win32');
+ $postgres->AddFile('src/backend/utils/fmgrtab.c');
+ $postgres->ReplaceFile('src/backend/port/pg_sema.c',
+ 'src/backend/port/win32_sema.c');
+ $postgres->ReplaceFile('src/backend/port/pg_shmem.c',
+ 'src/backend/port/win32_shmem.c');
+ $postgres->AddFiles('src/port', @pgportfiles);
+ $postgres->AddFiles('src/common', @pgcommonbkndfiles);
+ $postgres->AddDir('src/timezone');
+
+ # We need source files from src/timezone, but that directory's resource
+ # file pertains to "zic", not to the backend.
+ $postgres->RemoveFile('src/timezone/win32ver.rc');
+ $postgres->AddFiles('src/backend/parser', 'scan.l', 'gram.y');
+ $postgres->AddFiles('src/backend/bootstrap', 'bootscanner.l',
+ 'bootparse.y');
+ $postgres->AddFiles('src/backend/utils/misc', 'guc-file.l');
+ $postgres->AddFiles(
+ 'src/backend/replication', 'repl_scanner.l',
+ 'repl_gram.y', 'syncrep_scanner.l',
+ 'syncrep_gram.y');
+ $postgres->AddFiles('src/backend/utils/adt', 'jsonpath_scan.l',
+ 'jsonpath_gram.y');
+ $postgres->AddDefine('BUILDING_DLL');
+ $postgres->AddLibrary('secur32.lib');
+ $postgres->AddLibrary('ws2_32.lib');
+ $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
+ $postgres->FullExportDLL('postgres.lib');
+
+ # The OBJS scraper doesn't know about ifdefs, so remove appropriate files
+ # if building without OpenSSL.
+ if (!$solution->{options}->{openssl})
+ {
+ $postgres->RemoveFile('src/backend/libpq/be-secure-common.c');
+ $postgres->RemoveFile('src/backend/libpq/be-secure-openssl.c');
+ }
+ if (!$solution->{options}->{gss})
+ {
+ $postgres->RemoveFile('src/backend/libpq/be-gssapi-common.c');
+ $postgres->RemoveFile('src/backend/libpq/be-secure-gssapi.c');
+ }
+
+ my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
+ 'src/backend/snowball');
+
+ # This Makefile uses VPATH to find most source files in a subdirectory.
+ $snowball->RelocateFiles(
+ 'src/backend/snowball/libstemmer',
+ sub {
+ return shift !~ /(dict_snowball.c|win32ver.rc)$/;
+ });
+ $snowball->AddIncludeDir('src/include/snowball');
+ $snowball->AddReference($postgres);
+
+ my $plpgsql =
+ $solution->AddProject('plpgsql', 'dll', 'PLs', 'src/pl/plpgsql/src');
+ $plpgsql->AddFiles('src/pl/plpgsql/src', 'pl_gram.y');
+ $plpgsql->AddReference($postgres);
+
+ if ($solution->{options}->{tcl})
+ {
+ my $found = 0;
+ my $pltcl =
+ $solution->AddProject('pltcl', 'dll', 'PLs', 'src/pl/tcl');
+ $pltcl->AddIncludeDir($solution->{options}->{tcl} . '/include');
+ $pltcl->AddReference($postgres);
+
+ for my $tclver (qw(86t 86 85 84))
+ {
+ my $tcllib = $solution->{options}->{tcl} . "/lib/tcl$tclver.lib";
+ if (-e $tcllib)
+ {
+ $pltcl->AddLibrary($tcllib);
+ $found = 1;
+ last;
+ }
+ }
+ die "Unable to find $solution->{options}->{tcl}/lib/tcl<version>.lib"
+ unless $found;
+ }
+
+ $libpq = $solution->AddProject('libpq', 'dll', 'interfaces',
+ 'src/interfaces/libpq');
+ $libpq->AddDefine('FRONTEND');
+ $libpq->AddDefine('UNSAFE_STAT_OK');
+ $libpq->AddIncludeDir('src/port');
+ $libpq->AddLibrary('secur32.lib');
+ $libpq->AddLibrary('ws2_32.lib');
+ $libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
+ $libpq->UseDef('src/interfaces/libpq/libpqdll.def');
+ $libpq->AddReference($libpgcommon, $libpgport);
+
+ # The OBJS scraper doesn't know about ifdefs, so remove appropriate files
+ # if building without OpenSSL.
+ if (!$solution->{options}->{openssl})
+ {
+ $libpq->RemoveFile('src/interfaces/libpq/fe-secure-common.c');
+ $libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c');
+ }
+ if (!$solution->{options}->{gss})
+ {
+ $libpq->RemoveFile('src/interfaces/libpq/fe-gssapi-common.c');
+ $libpq->RemoveFile('src/interfaces/libpq/fe-secure-gssapi.c');
+ }
+
+ my $libpqwalreceiver =
+ $solution->AddProject('libpqwalreceiver', 'dll', '',
+ 'src/backend/replication/libpqwalreceiver');
+ $libpqwalreceiver->AddIncludeDir('src/interfaces/libpq');
+ $libpqwalreceiver->AddReference($postgres, $libpq);
+
+ my $libpq_testclient =
+ $solution->AddProject('libpq_testclient', 'exe', 'misc',
+ 'src/interfaces/libpq/test');
+ $libpq_testclient->AddFile(
+ 'src/interfaces/libpq/test/libpq_testclient.c');
+ $libpq_testclient->AddIncludeDir('src/interfaces/libpq');
+ $libpq_testclient->AddReference($libpgport, $libpq);
+ $libpq_testclient->AddLibrary('ws2_32.lib');
+
+ my $libpq_uri_regress =
+ $solution->AddProject('libpq_uri_regress', 'exe', 'misc',
+ 'src/interfaces/libpq/test');
+ $libpq_uri_regress->AddFile(
+ 'src/interfaces/libpq/test/libpq_uri_regress.c');
+ $libpq_uri_regress->AddIncludeDir('src/interfaces/libpq');
+ $libpq_uri_regress->AddReference($libpgport, $libpq);
+ $libpq_uri_regress->AddLibrary('ws2_32.lib');
+
+ my $pgoutput = $solution->AddProject('pgoutput', 'dll', '',
+ 'src/backend/replication/pgoutput');
+ $pgoutput->AddReference($postgres);
+
+ my $pgtypes = $solution->AddProject(
+ 'libpgtypes', 'dll',
+ 'interfaces', 'src/interfaces/ecpg/pgtypeslib');
+ $pgtypes->AddDefine('FRONTEND');
+ $pgtypes->AddReference($libpgcommon, $libpgport);
+ $pgtypes->UseDef('src/interfaces/ecpg/pgtypeslib/pgtypeslib.def');
+ $pgtypes->AddIncludeDir('src/interfaces/ecpg/include');
+
+ my $libecpg = $solution->AddProject('libecpg', 'dll', 'interfaces',
+ 'src/interfaces/ecpg/ecpglib');
+ $libecpg->AddDefine('FRONTEND');
+ $libecpg->AddIncludeDir('src/interfaces/ecpg/include');
+ $libecpg->AddIncludeDir('src/interfaces/libpq');
+ $libecpg->AddIncludeDir('src/port');
+ $libecpg->UseDef('src/interfaces/ecpg/ecpglib/ecpglib.def');
+ $libecpg->AddLibrary('ws2_32.lib');
+ $libecpg->AddReference($libpq, $pgtypes, $libpgport);
+
+ my $libecpgcompat = $solution->AddProject(
+ 'libecpg_compat', 'dll',
+ 'interfaces', 'src/interfaces/ecpg/compatlib');
+ $libecpgcompat->AddDefine('FRONTEND');
+ $libecpgcompat->AddIncludeDir('src/interfaces/ecpg/include');
+ $libecpgcompat->AddIncludeDir('src/interfaces/libpq');
+ $libecpgcompat->UseDef('src/interfaces/ecpg/compatlib/compatlib.def');
+ $libecpgcompat->AddReference($pgtypes, $libecpg, $libpgport,
+ $libpgcommon);
+
+ my $ecpg = $solution->AddProject('ecpg', 'exe', 'interfaces',
+ 'src/interfaces/ecpg/preproc');
+ $ecpg->AddIncludeDir('src/interfaces/ecpg/include');
+ $ecpg->AddIncludeDir('src/interfaces/ecpg/ecpglib');
+ $ecpg->AddIncludeDir('src/interfaces/libpq');
+ $ecpg->AddPrefixInclude('src/interfaces/ecpg/preproc');
+ $ecpg->AddFiles('src/interfaces/ecpg/preproc', 'pgc.l', 'preproc.y');
+ $ecpg->AddReference($libpgcommon, $libpgport);
+
+ my $pgregress_ecpg =
+ $solution->AddProject('pg_regress_ecpg', 'exe', 'misc');
+ $pgregress_ecpg->AddFile('src/interfaces/ecpg/test/pg_regress_ecpg.c');
+ $pgregress_ecpg->AddFile('src/test/regress/pg_regress.c');
+ $pgregress_ecpg->AddIncludeDir('src/port');
+ $pgregress_ecpg->AddIncludeDir('src/test/regress');
+ $pgregress_ecpg->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
+ $pgregress_ecpg->AddLibrary('ws2_32.lib');
+ $pgregress_ecpg->AddDirResourceFile('src/interfaces/ecpg/test');
+ $pgregress_ecpg->AddReference($libpgcommon, $libpgport);
+
+ my $isolation_tester =
+ $solution->AddProject('isolationtester', 'exe', 'misc');
+ $isolation_tester->AddFile('src/test/isolation/isolationtester.c');
+ $isolation_tester->AddFile('src/test/isolation/specparse.y');
+ $isolation_tester->AddFile('src/test/isolation/specscanner.l');
+ $isolation_tester->AddFile('src/test/isolation/specparse.c');
+ $isolation_tester->AddIncludeDir('src/test/isolation');
+ $isolation_tester->AddIncludeDir('src/port');
+ $isolation_tester->AddIncludeDir('src/test/regress');
+ $isolation_tester->AddIncludeDir('src/interfaces/libpq');
+ $isolation_tester->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
+ $isolation_tester->AddLibrary('ws2_32.lib');
+ $isolation_tester->AddDirResourceFile('src/test/isolation');
+ $isolation_tester->AddReference($libpq, $libpgcommon, $libpgport);
+
+ my $pgregress_isolation =
+ $solution->AddProject('pg_isolation_regress', 'exe', 'misc');
+ $pgregress_isolation->AddFile('src/test/isolation/isolation_main.c');
+ $pgregress_isolation->AddFile('src/test/regress/pg_regress.c');
+ $pgregress_isolation->AddIncludeDir('src/port');
+ $pgregress_isolation->AddIncludeDir('src/test/regress');
+ $pgregress_isolation->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
+ $pgregress_isolation->AddLibrary('ws2_32.lib');
+ $pgregress_isolation->AddDirResourceFile('src/test/isolation');
+ $pgregress_isolation->AddReference($libpgcommon, $libpgport);
+
+ # src/bin
+ my $D;
+ opendir($D, 'src/bin') || croak "Could not opendir on src/bin!\n";
+ while (my $d = readdir($D))
+ {
+ next if ($d =~ /^\./);
+ next unless (-f "src/bin/$d/Makefile");
+ next if (grep { /^$d$/ } @frontend_excludes);
+ AddSimpleFrontend($d);
+ }
+
+ my $pgbasebackup = AddSimpleFrontend('pg_basebackup', 1);
+ # This list of files has to match BBOBJS in pg_basebackup's Makefile.
+ $pgbasebackup->AddFile('src/bin/pg_basebackup/pg_basebackup.c');
+ $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_file.c');
+ $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_gzip.c');
+ $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_inject.c');
+ $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_lz4.c');
+ $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_zstd.c');
+ $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_tar.c');
+ $pgbasebackup->AddLibrary('ws2_32.lib');
+
+ my $pgreceivewal = AddSimpleFrontend('pg_basebackup', 1);
+ $pgreceivewal->{name} = 'pg_receivewal';
+ $pgreceivewal->AddFile('src/bin/pg_basebackup/pg_receivewal.c');
+ $pgreceivewal->AddLibrary('ws2_32.lib');
+
+ my $pgrecvlogical = AddSimpleFrontend('pg_basebackup', 1);
+ $pgrecvlogical->{name} = 'pg_recvlogical';
+ $pgrecvlogical->AddFile('src/bin/pg_basebackup/pg_recvlogical.c');
+ $pgrecvlogical->AddLibrary('ws2_32.lib');
+
+ my $pgrewind = AddSimpleFrontend('pg_rewind', 1);
+ $pgrewind->{name} = 'pg_rewind';
+ $pgrewind->AddFile('src/backend/access/transam/xlogreader.c');
+ $pgrewind->AddLibrary('ws2_32.lib');
+ $pgrewind->AddDefine('FRONTEND');
+
+ my $pgevent = $solution->AddProject('pgevent', 'dll', 'bin');
+ $pgevent->AddFiles('src/bin/pgevent', 'pgevent.c', 'pgmsgevent.rc');
+ $pgevent->AddResourceFile('src/bin/pgevent', 'Eventlog message formatter',
+ 'win32');
+ $pgevent->RemoveFile('src/bin/pgevent/win32ver.rc');
+ $pgevent->UseDef('src/bin/pgevent/pgevent.def');
+ $pgevent->DisableLinkerWarnings('4104');
+
+ my $pgdump = AddSimpleFrontend('pg_dump', 1);
+ $pgdump->AddIncludeDir('src/backend');
+ $pgdump->AddFile('src/bin/pg_dump/pg_dump.c');
+ $pgdump->AddFile('src/bin/pg_dump/common.c');
+ $pgdump->AddFile('src/bin/pg_dump/pg_dump_sort.c');
+ $pgdump->AddLibrary('ws2_32.lib');
+
+ my $pgdumpall = AddSimpleFrontend('pg_dump', 1);
+
+ # pg_dumpall doesn't use the files in the Makefile's $(OBJS), unlike
+ # pg_dump and pg_restore.
+ # So remove their sources from the object, keeping the other setup that
+ # AddSimpleFrontend() has done.
+ my @nodumpall = grep { m!src/bin/pg_dump/.*\.c$! }
+ keys %{ $pgdumpall->{files} };
+ delete @{ $pgdumpall->{files} }{@nodumpall};
+ $pgdumpall->{name} = 'pg_dumpall';
+ $pgdumpall->AddIncludeDir('src/backend');
+ $pgdumpall->AddFile('src/bin/pg_dump/pg_dumpall.c');
+ $pgdumpall->AddFile('src/bin/pg_dump/dumputils.c');
+ $pgdumpall->AddLibrary('ws2_32.lib');
+
+ my $pgrestore = AddSimpleFrontend('pg_dump', 1);
+ $pgrestore->{name} = 'pg_restore';
+ $pgrestore->AddIncludeDir('src/backend');
+ $pgrestore->AddFile('src/bin/pg_dump/pg_restore.c');
+ $pgrestore->AddLibrary('ws2_32.lib');
+
+ my $zic = $solution->AddProject('zic', 'exe', 'utils');
+ $zic->AddFiles('src/timezone', 'zic.c');
+ $zic->AddDirResourceFile('src/timezone');
+ $zic->AddReference($libpgcommon, $libpgport);
+
+ if (!$solution->{options}->{xml})
+ {
+ push @contrib_excludes, 'xml2';
+ }
+
+ if (!$solution->{options}->{openssl})
+ {
+ push @contrib_excludes, 'sslinfo', 'ssl_passphrase_callback',
+ 'pgcrypto';
+ }
+
+ if (!$solution->{options}->{uuid})
+ {
+ push @contrib_excludes, 'uuid-ossp';
+ }
+
+ foreach my $subdir ('contrib', 'src/test/modules')
+ {
+ opendir($D, $subdir) || croak "Could not opendir on $subdir!\n";
+ while (my $d = readdir($D))
+ {
+ next if ($d =~ /^\./);
+ next unless (-f "$subdir/$d/Makefile");
+ next if (grep { /^$d$/ } @contrib_excludes);
+ AddContrib($subdir, $d);
+ }
+ closedir($D);
+ }
+
+ # Build Perl and Python modules after contrib/ modules to satisfy some
+ # dependencies with transform contrib modules, like hstore_plpython
+ # ltree_plpython and hstore_plperl.
+ if ($solution->{options}->{python})
+ {
+
+ # Attempt to get python version and location.
+ # Assume python.exe in specified dir.
+ my $pythonprog = "import sys;print(sys.prefix);"
+ . "print(str(sys.version_info[0])+str(sys.version_info[1]))";
+ my $prefixcmd =
+ qq("$solution->{options}->{python}\\python" -c "$pythonprog");
+ my $pyout = `$prefixcmd`;
+ die "Could not query for python version!\n" if $?;
+ my ($pyprefix, $pyver) = split(/\r?\n/, $pyout);
+
+ # Sometimes (always?) if python is not present, the execution
+ # appears to work, but gives no data...
+ die "Failed to query python for version information\n"
+ if (!(defined($pyprefix) && defined($pyver)));
+
+ my $pymajorver = substr($pyver, 0, 1);
+
+ die
+ "Python version $pyver is too old (version 3 or later is required)"
+ if int($pymajorver) < 3;
+
+ my $plpython = $solution->AddProject('plpython' . $pymajorver,
+ 'dll', 'PLs', 'src/pl/plpython');
+ $plpython->AddIncludeDir($pyprefix . '/include');
+ $plpython->AddLibrary($pyprefix . "/Libs/python$pyver.lib");
+ $plpython->AddReference($postgres);
+
+ # Add transform modules dependent on plpython
+ my $hstore_plpython = AddTransformModule(
+ 'hstore_plpython' . $pymajorver, 'contrib/hstore_plpython',
+ 'plpython' . $pymajorver, 'src/pl/plpython',
+ 'hstore', 'contrib');
+ $hstore_plpython->AddDefine(
+ 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"');
+ my $jsonb_plpython = AddTransformModule(
+ 'jsonb_plpython' . $pymajorver, 'contrib/jsonb_plpython',
+ 'plpython' . $pymajorver, 'src/pl/plpython');
+ $jsonb_plpython->AddDefine(
+ 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"');
+ my $ltree_plpython = AddTransformModule(
+ 'ltree_plpython' . $pymajorver, 'contrib/ltree_plpython',
+ 'plpython' . $pymajorver, 'src/pl/plpython',
+ 'ltree', 'contrib');
+ $ltree_plpython->AddDefine(
+ 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"');
+ }
+
+ if ($solution->{options}->{perl})
+ {
+ my $plperlsrc = "src/pl/plperl/";
+ my $plperl =
+ $solution->AddProject('plperl', 'dll', 'PLs', 'src/pl/plperl');
+ $plperl->AddIncludeDir($solution->{options}->{perl} . '/lib/CORE');
+ $plperl->AddReference($postgres);
+
+ my $perl_path = $solution->{options}->{perl} . '\lib\CORE\*perl*';
+
+ # ActivePerl 5.16 provided perl516.lib; 5.18 provided libperl518.a
+ # Starting with ActivePerl 5.24, both perlnn.lib and libperlnn.a are provided.
+ # In this case, prefer .lib.
+ my @perl_libs =
+ reverse sort grep { /perl\d+\.lib$|libperl\d+\.a$/ }
+ glob($perl_path);
+ if (@perl_libs > 0)
+ {
+ $plperl->AddLibrary($perl_libs[0]);
+ }
+ else
+ {
+ die
+ "could not identify perl library version matching pattern $perl_path\n";
+ }
+
+ # Add defines from Perl's ccflags; see PGAC_CHECK_PERL_EMBED_CCFLAGS
+ my @perl_embed_ccflags;
+ foreach my $f (split(" ", $Config{ccflags}))
+ {
+ if ($f =~ /^-D[^_]/)
+ {
+ $f =~ s/\-D//;
+ push(@perl_embed_ccflags, $f);
+ }
+ }
+
+ # hack to prevent duplicate definitions of uid_t/gid_t
+ push(@perl_embed_ccflags, 'PLPERL_HAVE_UID_GID');
+ # prevent binary mismatch between MSVC built plperl and
+ # Strawberry or msys ucrt perl libraries
+ push(@perl_embed_ccflags, 'NO_THREAD_SAFE_LOCALE');
+
+ # Windows offers several 32-bit ABIs. Perl is sensitive to
+ # sizeof(time_t), one of the ABI dimensions. To get 32-bit time_t,
+ # use "cl -D_USE_32BIT_TIME_T" or plain "gcc". For 64-bit time_t, use
+ # "gcc -D__MINGW_USE_VC2005_COMPAT" or plain "cl". Before MSVC 2005,
+ # plain "cl" chose 32-bit time_t. PostgreSQL doesn't support building
+ # with pre-MSVC-2005 compilers, but it does support linking to Perl
+ # built with such a compiler. MSVC-built Perl 5.13.4 and later report
+ # -D_USE_32BIT_TIME_T in $Config{ccflags} if applicable, but
+ # MinGW-built Perl never reports -D_USE_32BIT_TIME_T despite typically
+ # needing it. Ignore the $Config{ccflags} opinion about
+ # -D_USE_32BIT_TIME_T, and use a runtime test to deduce the ABI Perl
+ # expects. Specifically, test use of PL_modglobal, which maps to a
+ # PerlInterpreter field whose position depends on sizeof(time_t).
+ if ($solution->{platform} eq 'Win32')
+ {
+ my $source_file = 'conftest.c';
+ my $obj = 'conftest.obj';
+ my $exe = 'conftest.exe';
+ my @conftest = ($source_file, $obj, $exe);
+ push @unlink_on_exit, @conftest;
+ unlink $source_file;
+ open my $o, '>', $source_file
+ || croak "Could not write to $source_file";
+ print $o '
+ /* compare to plperl.h */
+ #define __inline__ __inline
+ #define PERL_NO_GET_CONTEXT
+ #include <EXTERN.h>
+ #include <perl.h>
+
+ int
+ main(int argc, char **argv)
+ {
+ int dummy_argc = 1;
+ char *dummy_argv[1] = {""};
+ char *dummy_env[1] = {NULL};
+ static PerlInterpreter *interp;
+
+ PERL_SYS_INIT3(&dummy_argc, (char ***) &dummy_argv,
+ (char ***) &dummy_env);
+ interp = perl_alloc();
+ perl_construct(interp);
+ {
+ dTHX;
+ const char key[] = "dummy";
+
+ PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
+ hv_store(PL_modglobal, key, sizeof(key) - 1, newSViv(1), 0);
+ return hv_fetch(PL_modglobal, key, sizeof(key) - 1, 0) == NULL;
+ }
+ }
+';
+ close $o;
+
+ # Build $source_file with a given #define, and return a true value
+ # if a run of the resulting binary exits successfully.
+ my $try_define = sub {
+ my $define = shift;
+
+ unlink $obj, $exe;
+ my @cmd = (
+ 'cl',
+ '-I' . $solution->{options}->{perl} . '/lib/CORE',
+ (map { "-D$_" } @perl_embed_ccflags, $define || ()),
+ $source_file,
+ '/link',
+ $perl_libs[0]);
+ my $compile_output = `@cmd 2>&1`;
+ -f $exe || die "Failed to build Perl test:\n$compile_output";
+
+ {
+
+ # Some builds exhibit runtime failure through Perl warning
+ # 'Can't spawn "conftest.exe"'; suppress that.
+ no warnings;
+
+ no strict 'subs'; ## no critic (ProhibitNoStrict)
+
+ # Disable error dialog boxes like we do in the postmaster.
+ # Here, we run code that triggers relevant errors.
+ use
+ if ($^O eq "MSWin32"), 'Win32API::File',
+ qw(SetErrorMode :SEM_);
+ my $oldmode = SetErrorMode(
+ SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
+ system(".\\$exe");
+ SetErrorMode($oldmode);
+ }
+
+ return !($? >> 8);
+ };
+
+ my $define_32bit_time = '_USE_32BIT_TIME_T';
+ my $ok_now = $try_define->(undef);
+ my $ok_32bit = $try_define->($define_32bit_time);
+ unlink @conftest;
+ if (!$ok_now && !$ok_32bit)
+ {
+
+ # Unsupported configuration. Since we used %Config from the
+ # Perl running the build scripts, this is expected if
+ # attempting to link with some other Perl.
+ die "Perl test fails with or without -D$define_32bit_time";
+ }
+ elsif ($ok_now && $ok_32bit)
+ {
+
+ # Resulting build may work, but it's especially important to
+ # verify with "vcregress plcheck". A refined test may avoid
+ # this outcome.
+ warn "Perl test passes with or without -D$define_32bit_time";
+ }
+ elsif ($ok_32bit)
+ {
+ push(@perl_embed_ccflags, $define_32bit_time);
+ } # else $ok_now, hence no flag required
+ }
+
+ print "CFLAGS recommended by Perl: $Config{ccflags}\n";
+ print "CFLAGS to compile embedded Perl: ",
+ (join ' ', map { "-D$_" } @perl_embed_ccflags), "\n";
+ foreach my $f (@perl_embed_ccflags)
+ {
+ $plperl->AddDefine($f);
+ }
+
+ foreach my $xs ('SPI.xs', 'Util.xs')
+ {
+ (my $xsc = $xs) =~ s/\.xs/.c/;
+ if (Solution::IsNewer("$plperlsrc$xsc", "$plperlsrc$xs"))
+ {
+ my $xsubppdir = first { -e "$_/ExtUtils/xsubpp" } (@INC);
+ print "Building $plperlsrc$xsc...\n";
+ system( $solution->{options}->{perl}
+ . '/bin/perl '
+ . "$xsubppdir/ExtUtils/xsubpp -typemap "
+ . $solution->{options}->{perl}
+ . '/lib/ExtUtils/typemap '
+ . "$plperlsrc$xs "
+ . ">$plperlsrc$xsc");
+ if ((!(-f "$plperlsrc$xsc")) || -z "$plperlsrc$xsc")
+ {
+ unlink("$plperlsrc$xsc"); # if zero size
+ die "Failed to create $xsc.\n";
+ }
+ }
+ }
+ if (Solution::IsNewer(
+ 'src/pl/plperl/perlchunks.h',
+ 'src/pl/plperl/plc_perlboot.pl')
+ || Solution::IsNewer(
+ 'src/pl/plperl/perlchunks.h',
+ 'src/pl/plperl/plc_trusted.pl'))
+ {
+ print 'Building src/pl/plperl/perlchunks.h ...' . "\n";
+ my $basedir = getcwd;
+ chdir 'src/pl/plperl';
+ system( $solution->{options}->{perl}
+ . '/bin/perl '
+ . 'text2macro.pl '
+ . '--strip="^(\#.*|\s*)$$" '
+ . 'plc_perlboot.pl plc_trusted.pl '
+ . '>perlchunks.h');
+ chdir $basedir;
+ if ((!(-f 'src/pl/plperl/perlchunks.h'))
+ || -z 'src/pl/plperl/perlchunks.h')
+ {
+ unlink('src/pl/plperl/perlchunks.h'); # if zero size
+ die 'Failed to create perlchunks.h' . "\n";
+ }
+ }
+ if (Solution::IsNewer(
+ 'src/pl/plperl/plperl_opmask.h',
+ 'src/pl/plperl/plperl_opmask.pl'))
+ {
+ print 'Building src/pl/plperl/plperl_opmask.h ...' . "\n";
+ my $basedir = getcwd;
+ chdir 'src/pl/plperl';
+ system( $solution->{options}->{perl}
+ . '/bin/perl '
+ . 'plperl_opmask.pl '
+ . 'plperl_opmask.h');
+ chdir $basedir;
+ if ((!(-f 'src/pl/plperl/plperl_opmask.h'))
+ || -z 'src/pl/plperl/plperl_opmask.h')
+ {
+ unlink('src/pl/plperl/plperl_opmask.h'); # if zero size
+ die 'Failed to create plperl_opmask.h' . "\n";
+ }
+ }
+
+ # Add transform modules dependent on plperl
+ my $bool_plperl = AddTransformModule(
+ 'bool_plperl', 'contrib/bool_plperl',
+ 'plperl', 'src/pl/plperl');
+ my $hstore_plperl = AddTransformModule(
+ 'hstore_plperl', 'contrib/hstore_plperl',
+ 'plperl', 'src/pl/plperl',
+ 'hstore', 'contrib');
+ my $jsonb_plperl = AddTransformModule(
+ 'jsonb_plperl', 'contrib/jsonb_plperl',
+ 'plperl', 'src/pl/plperl');
+
+ foreach my $f (@perl_embed_ccflags)
+ {
+ $bool_plperl->AddDefine($f);
+ $hstore_plperl->AddDefine($f);
+ $jsonb_plperl->AddDefine($f);
+ }
+ }
+
+ my $mf =
+ Project::read_file('src/backend/utils/mb/conversion_procs/Makefile');
+ $mf =~ s{\\\r?\n}{}g;
+ $mf =~ m{SUBDIRS\s*=\s*(.*)$}m
+ || die 'Could not match in conversion makefile' . "\n";
+ foreach my $sub (split /\s+/, $1)
+ {
+ my $dir = 'src/backend/utils/mb/conversion_procs/' . $sub;
+ my $p = $solution->AddProject($sub, 'dll', 'conversion procs', $dir);
+ $p->AddFile("$dir/$sub.c"); # implicit source file
+ $p->AddReference($postgres);
+ }
+
+ $mf = Project::read_file('src/bin/scripts/Makefile');
+ $mf =~ s{\\\r?\n}{}g;
+ $mf =~ m{PROGRAMS\s*=\s*(.*)$}m
+ || die 'Could not match in bin/scripts/Makefile' . "\n";
+ foreach my $prg (split /\s+/, $1)
+ {
+ my $proj = $solution->AddProject($prg, 'exe', 'bin');
+ $mf =~ m{$prg\s*:\s*(.*)$}m
+ || die 'Could not find script define for $prg' . "\n";
+ my @files = split /\s+/, $1;
+ foreach my $f (@files)
+ {
+ $f =~ s/\.o$/\.c/;
+ if ($f =~ /\.c$/)
+ {
+ $proj->AddFile('src/bin/scripts/' . $f);
+ }
+ }
+ $proj->AddIncludeDir('src/interfaces/libpq');
+ $proj->AddReference($libpq, $libpgfeutils, $libpgcommon, $libpgport);
+ $proj->AddDirResourceFile('src/bin/scripts');
+ $proj->AddLibrary('ws2_32.lib');
+ }
+
+ # Regression DLL and EXE
+ my $regress = $solution->AddProject('regress', 'dll', 'misc');
+ $regress->AddFile('src/test/regress/regress.c');
+ $regress->AddDirResourceFile('src/test/regress');
+ $regress->AddReference($postgres);
+
+ my $pgregress = $solution->AddProject('pg_regress', 'exe', 'misc');
+ $pgregress->AddFile('src/test/regress/pg_regress.c');
+ $pgregress->AddFile('src/test/regress/pg_regress_main.c');
+ $pgregress->AddIncludeDir('src/port');
+ $pgregress->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
+ $pgregress->AddLibrary('ws2_32.lib');
+ $pgregress->AddDirResourceFile('src/test/regress');
+ $pgregress->AddReference($libpgcommon, $libpgport);
+
+ # fix up pg_waldump once it's been set up
+ # files symlinked on Unix are copied on windows
+ my $pg_waldump = AddSimpleFrontend('pg_waldump');
+ $pg_waldump->AddDefine('FRONTEND');
+ foreach my $xf (glob('src/backend/access/rmgrdesc/*desc.c'))
+ {
+ $pg_waldump->AddFile($xf);
+ }
+ $pg_waldump->AddFile('src/backend/access/transam/xlogreader.c');
+
+ $solution->Save();
+ return $solution->{vcver};
+}
+
+#####################
+# Utility functions #
+#####################
+
+# Add a simple frontend project (exe)
+sub AddSimpleFrontend
+{
+ my $n = shift;
+ my $uselibpq = shift;
+
+ my $p = $solution->AddProject($n, 'exe', 'bin');
+ $p->AddDir('src/bin/' . $n);
+ $p->AddReference($libpgfeutils, $libpgcommon, $libpgport);
+ if ($uselibpq)
+ {
+ $p->AddIncludeDir('src/interfaces/libpq');
+ $p->AddReference($libpq);
+ }
+
+ # Adjust module definition using frontend variables
+ AdjustFrontendProj($p);
+
+ return $p;
+}
+
+# Add a simple transform module
+sub AddTransformModule
+{
+ my $n = shift;
+ my $n_src = shift;
+ my $pl_proj_name = shift;
+ my $pl_src = shift;
+ my $type_name = shift;
+ my $type_src = shift;
+
+ my $type_proj = undef;
+ if ($type_name)
+ {
+ foreach my $proj (@{ $solution->{projects}->{'contrib'} })
+ {
+ if ($proj->{name} eq $type_name)
+ {
+ $type_proj = $proj;
+ last;
+ }
+ }
+ die "could not find base module $type_name for transform module $n"
+ if (!defined($type_proj));
+ }
+
+ my $pl_proj = undef;
+ foreach my $proj (@{ $solution->{projects}->{'PLs'} })
+ {
+ if ($proj->{name} eq $pl_proj_name)
+ {
+ $pl_proj = $proj;
+ last;
+ }
+ }
+ die "could not find PL $pl_proj_name for transform module $n"
+ if (!defined($pl_proj));
+
+ my $p = $solution->AddProject($n, 'dll', 'contrib', $n_src);
+ for my $file (glob("$n_src/*.c"))
+ {
+ $p->AddFile($file);
+ }
+ $p->AddReference($postgres);
+
+ # Add PL dependencies
+ $p->AddIncludeDir($pl_src);
+ $p->AddReference($pl_proj);
+ $p->AddIncludeDir($_) for @{ $pl_proj->{includes} };
+ foreach my $pl_lib (@{ $pl_proj->{libraries} })
+ {
+ $p->AddLibrary($pl_lib);
+ }
+
+ # Add base module dependencies
+ if ($type_proj)
+ {
+ $p->AddIncludeDir($type_src);
+ $p->AddIncludeDir($_) for @{ $type_proj->{includes} };
+ foreach my $type_lib (@{ $type_proj->{libraries} })
+ {
+ $p->AddLibrary($type_lib);
+ }
+ $p->AddReference($type_proj);
+ }
+
+ return $p;
+}
+
+# Add a simple contrib project
+sub AddContrib
+{
+ my $subdir = shift;
+ my $n = shift;
+ my $mf = Project::read_file("$subdir/$n/Makefile");
+ my @projects = ();
+
+ if ($mf =~ /^MODULE_big\s*=\s*(.*)$/mg)
+ {
+ my $dn = $1;
+ my $proj = $solution->AddProject($dn, 'dll', 'contrib', "$subdir/$n");
+ $proj->AddReference($postgres);
+ AdjustContribProj($proj);
+ push @projects, $proj;
+ }
+ elsif ($mf =~ /^MODULES\s*=\s*(.*)$/mg)
+ {
+ foreach my $mod (split /\s+/, $1)
+ {
+ my $proj =
+ $solution->AddProject($mod, 'dll', 'contrib', "$subdir/$n");
+ my $filename = $mod . '.c';
+ $proj->AddFile("$subdir/$n/$filename");
+ $proj->AddReference($postgres);
+ AdjustContribProj($proj);
+ push @projects, $proj;
+ }
+ }
+ elsif ($mf =~ /^PROGRAM\s*=\s*(.*)$/mg)
+ {
+ my $proj = $solution->AddProject($1, 'exe', 'contrib', "$subdir/$n");
+ AdjustContribProj($proj);
+ push @projects, $proj;
+ }
+ else
+ {
+ croak "Could not determine contrib module type for $n\n";
+ }
+
+ # Process custom compiler flags
+ if ( $mf =~ /^PG_CPPFLAGS\s*=\s*(.*)$/mg
+ || $mf =~ /^override\s*CPPFLAGS\s*[+:]?=\s*(.*)$/mg)
+ {
+ foreach my $flag (split /\s+/, $1)
+ {
+ if ($flag =~ /^-D(.*)$/)
+ {
+ foreach my $proj (@projects)
+ {
+ $proj->AddDefine($1);
+ }
+ }
+ elsif ($flag =~ /^-I(.*)$/)
+ {
+ if ($1 eq '$(libpq_srcdir)')
+ {
+ foreach my $proj (@projects)
+ {
+ $proj->AddIncludeDir('src/interfaces/libpq');
+ $proj->AddReference($libpq);
+ }
+ }
+ }
+ }
+ }
+
+ if ($mf =~ /^SHLIB_LINK_INTERNAL\s*[+:]?=\s*(.*)$/mg)
+ {
+ foreach my $lib (split /\s+/, $1)
+ {
+ if ($lib eq '$(libpq)')
+ {
+ foreach my $proj (@projects)
+ {
+ $proj->AddIncludeDir('src/interfaces/libpq');
+ $proj->AddReference($libpq);
+ }
+ }
+ }
+ }
+
+ if ($mf =~ /^PG_LIBS_INTERNAL\s*[+:]?=\s*(.*)$/mg)
+ {
+ foreach my $lib (split /\s+/, $1)
+ {
+ if ($lib eq '$(libpq_pgport)')
+ {
+ foreach my $proj (@projects)
+ {
+ $proj->AddReference($libpgport);
+ $proj->AddReference($libpgcommon);
+ }
+ }
+ }
+ }
+
+ foreach my $line (split /\n/, $mf)
+ {
+ if ($line =~ /^[A-Za-z0-9_]*\.o:\s(.*)/)
+ {
+ foreach my $file (split /\s+/, $1)
+ {
+ foreach my $proj (@projects)
+ {
+ $proj->AddDependantFiles("$subdir/$n/$file");
+ }
+ }
+ }
+ }
+
+ # Are there any output data files to build?
+ GenerateContribSqlFiles($n, $mf);
+ return;
+}
+
+sub GenerateContribSqlFiles
+{
+ my $n = shift;
+ my $mf = shift;
+ $mf =~ s{\\\r?\n}{}g;
+ if ($mf =~ /^DATA_built\s*=\s*(.*)$/mg)
+ {
+ my $l = $1;
+
+ # Strip out $(addsuffix) rules
+ if (index($l, '$(addsuffix ') >= 0)
+ {
+ my $pcount = 0;
+ my $i;
+ for ($i = index($l, '$(addsuffix ') + 12; $i < length($l); $i++)
+ {
+ $pcount++ if (substr($l, $i, 1) eq '(');
+ $pcount-- if (substr($l, $i, 1) eq ')');
+ last if ($pcount < 0);
+ }
+ $l =
+ substr($l, 0, index($l, '$(addsuffix ')) . substr($l, $i + 1);
+ }
+
+ foreach my $d (split /\s+/, $l)
+ {
+ my $in = "$d.in";
+ my $out = "$d";
+
+ if (Solution::IsNewer("contrib/$n/$out", "contrib/$n/$in"))
+ {
+ print "Building $out from $in (contrib/$n)...\n";
+ my $cont = Project::read_file("contrib/$n/$in");
+ my $dn = $out;
+ $dn =~ s/\.sql$//;
+ $cont =~ s/MODULE_PATHNAME/\$libdir\/$dn/g;
+ my $o;
+ open($o, '>', "contrib/$n/$out")
+ || croak "Could not write to contrib/$n/$d";
+ print $o $cont;
+ close($o);
+ }
+ }
+ }
+ return;
+}
+
+sub AdjustContribProj
+{
+ my $proj = shift;
+ AdjustModule(
+ $proj, $contrib_defines,
+ \@contrib_uselibpq, \@contrib_uselibpgport,
+ \@contrib_uselibpgcommon, $contrib_extralibs,
+ $contrib_extrasource, $contrib_extraincludes);
+ return;
+}
+
+sub AdjustFrontendProj
+{
+ my $proj = shift;
+ AdjustModule(
+ $proj, $frontend_defines,
+ \@frontend_uselibpq, \@frontend_uselibpgport,
+ \@frontend_uselibpgcommon, $frontend_extralibs,
+ $frontend_extrasource, $frontend_extraincludes);
+ return;
+}
+
+sub AdjustModule
+{
+ my $proj = shift;
+ my $module_defines = shift;
+ my $module_uselibpq = shift;
+ my $module_uselibpgport = shift;
+ my $module_uselibpgcommon = shift;
+ my $module_extralibs = shift;
+ my $module_extrasource = shift;
+ my $module_extraincludes = shift;
+ my $n = $proj->{name};
+
+ if ($module_defines->{$n})
+ {
+ foreach my $d ($module_defines->{$n})
+ {
+ $proj->AddDefine($d);
+ }
+ }
+ if (grep { /^$n$/ } @{$module_uselibpq})
+ {
+ $proj->AddIncludeDir('src\interfaces\libpq');
+ $proj->AddReference($libpq);
+ }
+ if (grep { /^$n$/ } @{$module_uselibpgport})
+ {
+ $proj->AddReference($libpgport);
+ }
+ if (grep { /^$n$/ } @{$module_uselibpgcommon})
+ {
+ $proj->AddReference($libpgcommon);
+ }
+ if ($module_extralibs->{$n})
+ {
+ foreach my $l (@{ $module_extralibs->{$n} })
+ {
+ $proj->AddLibrary($l);
+ }
+ }
+ if ($module_extraincludes->{$n})
+ {
+ foreach my $i (@{ $module_extraincludes->{$n} })
+ {
+ $proj->AddIncludeDir($i);
+ }
+ }
+ if ($module_extrasource->{$n})
+ {
+ foreach my $i (@{ $module_extrasource->{$n} })
+ {
+ print "Files $i\n";
+ $proj->AddFile($i);
+ }
+ }
+ return;
+}
+
+END
+{
+ unlink @unlink_on_exit;
+}
+
+1;
diff --git a/src/tools/msvc/Project.pm b/src/tools/msvc/Project.pm
new file mode 100644
index 0000000..570bab5
--- /dev/null
+++ b/src/tools/msvc/Project.pm
@@ -0,0 +1,494 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+package Project;
+
+#
+# Package that encapsulates a Visual C++ project file generation
+#
+# src/tools/msvc/Project.pm
+#
+use Carp;
+use strict;
+use warnings;
+use File::Basename;
+
+sub _new
+{
+ my ($classname, $name, $type, $solution) = @_;
+ my $good_types = {
+ lib => 1,
+ exe => 1,
+ dll => 1,
+ };
+ confess("Bad project type: $type\n") unless exists $good_types->{$type};
+ my $self = {
+ name => $name,
+ type => $type,
+ guid => $^O eq "MSWin32" ? Win32::GuidGen() : 'FAKE',
+ files => {},
+ references => [],
+ libraries => [],
+ suffixlib => [],
+ includes => [],
+ prefixincludes => '',
+ defines => ';',
+ solution => $solution,
+ disablewarnings => '4018;4244;4273;4101;4102;4090;4267',
+ disablelinkerwarnings => '',
+ platform => $solution->{platform},
+ };
+
+ bless($self, $classname);
+ return $self;
+}
+
+sub AddFile
+{
+ my ($self, $filename) = @_;
+
+ $self->FindAndAddAdditionalFiles($filename);
+ $self->{files}->{$filename} = 1;
+ return;
+}
+
+sub AddDependantFiles
+{
+ my ($self, $filename) = @_;
+
+ $self->FindAndAddAdditionalFiles($filename);
+ return;
+}
+
+sub AddFiles
+{
+ my $self = shift;
+ my $dir = shift;
+
+ while (my $f = shift)
+ {
+ $self->AddFile($dir . "/" . $f, 1);
+ }
+ return;
+}
+
+# Handle Makefile rules by searching for other files which exist with the same
+# name but a different file extension and add those files too.
+sub FindAndAddAdditionalFiles
+{
+ my $self = shift;
+ my $fname = shift;
+ $fname =~ /(.*)(\.[^.]+)$/;
+ my $filenoext = $1;
+ my $fileext = $2;
+
+ # For .c files, check if either a .l or .y file of the same name
+ # exists and add that too.
+ if ($fileext eq ".c")
+ {
+ my $file = $filenoext . ".l";
+ if (-e $file)
+ {
+ $self->AddFile($file);
+ }
+
+ $file = $filenoext . ".y";
+ if (-e $file)
+ {
+ $self->AddFile($file);
+ }
+ }
+}
+
+sub ReplaceFile
+{
+ my ($self, $filename, $newname) = @_;
+ my $re = "\\/$filename\$";
+
+ foreach my $file (keys %{ $self->{files} })
+ {
+
+ # Match complete filename
+ if ($filename =~ m!/!)
+ {
+ if ($file eq $filename)
+ {
+ delete $self->{files}{$file};
+ $self->AddFile($newname);
+ return;
+ }
+ }
+ elsif ($file =~ m/($re)/)
+ {
+ delete $self->{files}{$file};
+ $self->AddFile("$newname/$filename");
+ return;
+ }
+ }
+ confess("Could not find file $filename to replace\n");
+}
+
+sub RemoveFile
+{
+ my ($self, $filename) = @_;
+ my $orig = scalar keys %{ $self->{files} };
+ delete $self->{files}->{$filename};
+ if ($orig > scalar keys %{ $self->{files} })
+ {
+ return;
+ }
+ confess("Could not find file $filename to remove\n");
+}
+
+sub RelocateFiles
+{
+ my ($self, $targetdir, $proc) = @_;
+ foreach my $f (keys %{ $self->{files} })
+ {
+ my $r = &$proc($f);
+ if ($r)
+ {
+ $self->RemoveFile($f);
+ $self->AddFile($targetdir . '/' . basename($f));
+ }
+ }
+ return;
+}
+
+sub AddReference
+{
+ my $self = shift;
+
+ while (my $ref = shift)
+ {
+ if (!grep { $_ eq $ref } @{ $self->{references} })
+ {
+ push @{ $self->{references} }, $ref;
+ }
+ $self->AddLibrary(
+ "__CFGNAME__/" . $ref->{name} . "/" . $ref->{name} . ".lib");
+ }
+ return;
+}
+
+sub AddLibrary
+{
+ my ($self, $lib, $dbgsuffix) = @_;
+
+ # quote lib name if it has spaces and isn't already quoted
+ if ($lib =~ m/\s/ && $lib !~ m/^[&]quot;/)
+ {
+ $lib = '&quot;' . $lib . "&quot;";
+ }
+
+ if (!grep { $_ eq $lib } @{ $self->{libraries} })
+ {
+ push @{ $self->{libraries} }, $lib;
+ }
+
+ if ($dbgsuffix)
+ {
+ push @{ $self->{suffixlib} }, $lib;
+ }
+ return;
+}
+
+sub AddIncludeDir
+{
+ my ($self, $incstr) = @_;
+
+ foreach my $inc (split(/;/, $incstr))
+ {
+ if (!grep { $_ eq $inc } @{ $self->{includes} })
+ {
+ push @{ $self->{includes} }, $inc;
+ }
+ }
+ return;
+}
+
+sub AddPrefixInclude
+{
+ my ($self, $inc) = @_;
+
+ $self->{prefixincludes} = $inc . ';' . $self->{prefixincludes};
+ return;
+}
+
+sub AddDefine
+{
+ my ($self, $def) = @_;
+
+ $def =~ s/"/&quot;&quot;/g;
+ $self->{defines} .= $def . ';';
+ return;
+}
+
+sub FullExportDLL
+{
+ my ($self, $libname) = @_;
+
+ $self->{builddef} = 1;
+ $self->{def} = "./__CFGNAME__/$self->{name}/$self->{name}.def";
+ $self->{implib} = "__CFGNAME__/$self->{name}/$libname";
+ return;
+}
+
+sub UseDef
+{
+ my ($self, $def) = @_;
+
+ $self->{def} = $def;
+ return;
+}
+
+sub AddDir
+{
+ my ($self, $reldir) = @_;
+ my $mf = read_makefile($reldir);
+
+ $mf =~ s{\\\r?\n}{}g;
+ if ($mf =~ m{^(?:SUB)?DIRS[^=]*=\s*(.*)$}mg)
+ {
+ foreach my $subdir (split /\s+/, $1)
+ {
+ next
+ if $subdir eq "\$(top_builddir)/src/timezone"
+ ; #special case for non-standard include
+ next
+ if $reldir . "/" . $subdir eq "src/backend/port/darwin";
+
+ $self->AddDir($reldir . "/" . $subdir);
+ }
+ }
+ while ($mf =~ m{^(?:EXTRA_)?OBJS[^=]*=\s*(.*)$}m)
+ {
+ my $s = $1;
+ my $filter_re = qr{\$\(filter ([^,]+),\s+\$\(([^\)]+)\)\)};
+ while ($s =~ /$filter_re/)
+ {
+
+ # Process $(filter a b c, $(VAR)) expressions
+ my $list = $1;
+ my $filter = $2;
+ $list =~ s/\.o/\.c/g;
+ my @pieces = split /\s+/, $list;
+ my $matches = "";
+ foreach my $p (@pieces)
+ {
+
+ if ($filter eq "LIBOBJS")
+ {
+ no warnings qw(once);
+ if (grep(/$p/, @main::pgportfiles) == 1)
+ {
+ $p =~ s/\.c/\.o/;
+ $matches .= $p . " ";
+ }
+ }
+ else
+ {
+ confess "Unknown filter $filter\n";
+ }
+ }
+ $s =~ s/$filter_re/$matches/;
+ }
+ foreach my $f (split /\s+/, $s)
+ {
+ next if $f =~ /^\s*$/;
+ next if $f eq "\\";
+ next if $f =~ /\/SUBSYS.o$/;
+ $f =~ s/,$//
+ ; # Remove trailing comma that can show up from filter stuff
+ next unless $f =~ /.*\.o$/;
+ $f =~ s/\.o$/\.c/;
+ if ($f =~ /^\$\(top_builddir\)\/(.*)/)
+ {
+ $f = $1;
+ $self->AddFile($f);
+ }
+ else
+ {
+ $self->AddFile("$reldir/$f");
+ }
+ }
+ $mf =~ s{OBJS[^=]*=\s*(.*)$}{}m;
+ }
+
+ # Match rules that pull in source files from different directories, eg
+ # pgstrcasecmp.c rint.c snprintf.c: % : $(top_srcdir)/src/port/%
+ my $replace_re =
+ qr{^([^:\n\$]+\.c)\s*:\s*(?:%\s*: )?\$(\([^\)]+\))\/(.*)\/[^\/]+\n}m;
+ while ($mf =~ m{$replace_re}m)
+ {
+ my $match = $1;
+ my $top = $2;
+ my $target = $3;
+ my @pieces = split /\s+/, $match;
+ foreach my $fn (@pieces)
+ {
+ if ($top eq "(top_srcdir)")
+ {
+ eval { $self->ReplaceFile($fn, $target) };
+ }
+ elsif ($top eq "(backend_src)")
+ {
+ eval { $self->ReplaceFile($fn, "src/backend/$target") };
+ }
+ else
+ {
+ confess "Bad replacement top: $top, on line $_\n";
+ }
+ }
+ $mf =~ s{$replace_re}{}m;
+ }
+
+ $self->AddDirResourceFile($reldir);
+ return;
+}
+
+# If the directory's Makefile bears a description string, add a resource file.
+sub AddDirResourceFile
+{
+ my ($self, $reldir) = @_;
+ my $mf = read_makefile($reldir);
+
+ if ($mf =~ /^PGFILEDESC\s*=\s*\"([^\"]+)\"/m)
+ {
+ my $desc = $1;
+ my $ico;
+ if ($mf =~ /^PGAPPICON\s*=\s*(.*)$/m) { $ico = $1; }
+ $self->AddResourceFile($reldir, $desc, $ico);
+ }
+ return;
+}
+
+sub AddResourceFile
+{
+ my ($self, $dir, $desc, $ico) = @_;
+
+ my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
+ localtime(time);
+ my $d = sprintf("%02d%03d", ($year - 100), $yday);
+
+ if (Solution::IsNewer("$dir/win32ver.rc", 'src/port/win32ver.rc'))
+ {
+ print "Generating win32ver.rc for $dir\n";
+ open(my $i, '<', 'src/port/win32ver.rc')
+ || confess "Could not open win32ver.rc";
+ open(my $o, '>', "$dir/win32ver.rc")
+ || confess "Could not write win32ver.rc";
+ my $icostr = $ico ? "IDI_ICON ICON \"src/port/$ico.ico\"" : "";
+ while (<$i>)
+ {
+ s/FILEDESC/"$desc"/gm;
+ s/_ICO_/$icostr/gm;
+ s/(VERSION.*),0/$1,$d/;
+ if ($self->{type} eq "dll")
+ {
+ s/VFT_APP/VFT_DLL/gm;
+ my $name = $self->{name};
+ s/_INTERNAL_NAME_/"$name"/;
+ s/_ORIGINAL_NAME_/"$name.dll"/;
+ }
+ else
+ {
+ /_INTERNAL_NAME_/ && next;
+ /_ORIGINAL_NAME_/ && next;
+ }
+ print $o $_;
+ }
+ close($o);
+ close($i);
+ }
+ $self->AddFile("$dir/win32ver.rc");
+ return;
+}
+
+sub DisableLinkerWarnings
+{
+ my ($self, $warnings) = @_;
+
+ $self->{disablelinkerwarnings} .= ','
+ unless ($self->{disablelinkerwarnings} eq '');
+ $self->{disablelinkerwarnings} .= $warnings;
+ return;
+}
+
+sub Save
+{
+ my ($self) = @_;
+
+ # If doing DLL and haven't specified a DEF file, do a full export of all symbols
+ # in the project.
+ if ($self->{type} eq "dll" && !$self->{def})
+ {
+ $self->FullExportDLL($self->{name} . ".lib");
+ }
+
+ # Warning 4197 is about double exporting, disable this per
+ # http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99193
+ $self->DisableLinkerWarnings('4197') if ($self->{platform} eq 'x64');
+
+ # Dump the project
+ open(my $f, '>', "$self->{name}$self->{filenameExtension}")
+ || croak(
+ "Could not write to $self->{name}$self->{filenameExtension}\n");
+ $self->WriteHeader($f);
+ $self->WriteFiles($f);
+ $self->Footer($f);
+ close($f);
+ return;
+}
+
+sub GetAdditionalLinkerDependencies
+{
+ my ($self, $cfgname, $separator) = @_;
+ my $libcfg = (uc $cfgname eq "RELEASE") ? "MD" : "MDd";
+ my $libs = '';
+ foreach my $lib (@{ $self->{libraries} })
+ {
+ my $xlib = $lib;
+ foreach my $slib (@{ $self->{suffixlib} })
+ {
+ if ($slib eq $lib)
+ {
+ $xlib =~ s/\.lib$/$libcfg.lib/;
+ last;
+ }
+ }
+ $libs .= $xlib . $separator;
+ }
+ $libs =~ s/.$//;
+ $libs =~ s/__CFGNAME__/$cfgname/g;
+ return $libs;
+}
+
+# Utility function that loads a complete file
+sub read_file
+{
+ my $filename = shift;
+ my $F;
+ local $/ = undef;
+ open($F, '<', $filename) || croak "Could not open file $filename\n";
+ my $txt = <$F>;
+ close($F);
+
+ return $txt;
+}
+
+sub read_makefile
+{
+ my $reldir = shift;
+ my $F;
+ local $/ = undef;
+ open($F, '<', "$reldir/GNUmakefile")
+ || open($F, '<', "$reldir/Makefile")
+ || confess "Could not open $reldir/Makefile\n";
+ my $txt = <$F>;
+ close($F);
+
+ return $txt;
+}
+
+1;
diff --git a/src/tools/msvc/README b/src/tools/msvc/README
new file mode 100644
index 0000000..473d6f4
--- /dev/null
+++ b/src/tools/msvc/README
@@ -0,0 +1,99 @@
+src/tools/msvc/README
+
+MSVC build
+==========
+
+This directory contains the tools required to build PostgreSQL using
+Microsoft Visual Studio 2013 - 2022. This builds the whole backend, not just
+the libpq frontend library. For more information, see the documentation
+chapter "Installation on Windows" and the description below.
+
+
+Notes about Visual Studio Express
+---------------------------------
+To build PostgreSQL using Visual Studio Express, the Microsoft Windows SDK
+has to be installed. Since this is not included in the product
+originally, extra steps are needed to make it work.
+
+First, download and install a supported version of the Microsoft Windows SDK
+from www.microsoft.com (v8.1a or greater).
+
+Locate the files vcprojectengine.dll.express.config and
+vcprojectengine.dll.config in the vc\vcpackages directory of
+the Visual C++ Express installation. In these files, add the paths
+to the Platform SDK to the Include, Library and Path tags. Be sure
+to add them to the beginning of the list.
+
+This should work for both GUI and commandline builds, but a restart
+may be necessary.
+
+If you are using a recent version of the Microsoft Windows SDK that includes
+the compilers and build tools you probably don't even need Visual Studio
+Express to build PostgreSQL.
+
+
+Structure of the build tools
+----------------------------
+The tools for building PostgreSQL using Microsoft Visual Studio currently
+consist of the following files:
+
+- Configuration files -
+config_default.pl default configuration arguments
+
+A typical build environment has two more files, buildenv.pl and config.pl
+that contain the user's build environment settings and configuration
+arguments.
+
+
+- User tools -
+build.pl tool to build the binaries
+clean.bat batch file for cleaning up generated files
+install.pl tool to install the generated files
+mkvcbuild.pl tool to generate the Visual Studio build files
+vcregress.pl tool to run the regression tests
+
+
+- Internal tools -
+gendef.pl internal tool to generate .DEF files
+pgbison.pl internal tool to process .y files using bison
+pgflex.pl internal tool to process .l files using flex
+
+Many of those .pl files also have a corresponding .bat-wrapper that doesn't
+contain any additional logic.
+
+
+- Internal modules -
+Install.pm module containing the install logic
+Mkvcbuild.pm module containing the code to generate the Visual
+ Studio build (project/solution) files
+MSBuildProject.pm module containing the code to generate MSBuild based
+ project files (Visual Studio 2013 or greater)
+Project.pm module containing the common code to generate the
+ Visual Studio project files. Also provides the
+ common interface of all project file generators
+Solution.pm module containing the code to generate the Visual
+ Studio solution files.
+VSObjectFactory.pm factory module providing the code to create the
+ appropriate project/solution files for the current
+ environment
+
+
+Description of the internals of the Visual Studio build process
+---------------------------------------------------------------
+By typing 'build' the user starts the build.bat wrapper which simply passes
+its arguments to build.pl.
+In build.pl the user's buildenv.pl is used to set up the build environment
+(i. e. path to bison and flex). In addition his config.pl file is merged into
+config_default.pl to create the configuration arguments.
+These configuration arguments are passed over to Mkvcbuild::mkvcbuild
+(Mkvcbuild.pm) which creates the Visual Studio project and solution files.
+It does this by using VSObjectFactory::CreateSolution to create an object
+implementing the Solution interface (this could be either VS2013Solution,
+VS2015Solution, VS2017Solution, VS2019Solution or VS2022Solution, all in
+Solution.pm, depending on the user's build environment) and adding objects
+implementing the corresponding Project interface (VC2013Project,
+VC2015Project, VC2017Project, VC2019Project or VC2022Project from
+MSBuildProject.pm) to it. When Solution::Save is called, the implementations
+of Solution and Project save their content in the appropriate format.
+The final step of starting the appropriate build program (msbuild) is
+performed in build.pl again.
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
new file mode 100644
index 0000000..790f03b
--- /dev/null
+++ b/src/tools/msvc/Solution.pm
@@ -0,0 +1,1378 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+package Solution;
+
+#
+# Package that encapsulates a Visual C++ solution file generation
+#
+# src/tools/msvc/Solution.pm
+#
+use Carp;
+use strict;
+use warnings;
+use VSObjectFactory;
+
+no warnings qw(redefine); ## no critic
+
+sub _new
+{
+ my $classname = shift;
+ my $options = shift;
+ my $self = {
+ projects => {},
+ options => $options,
+ VisualStudioVersion => undef,
+ MinimumVisualStudioVersion => undef,
+ vcver => undef,
+ platform => undef,
+ };
+ bless($self, $classname);
+
+ $self->DeterminePlatform();
+
+ if ($options->{xslt} && !$options->{xml})
+ {
+ die "XSLT requires XML\n";
+ }
+ $options->{blocksize} = 8
+ unless $options->{blocksize}; # undef or 0 means default
+ die "Bad blocksize $options->{blocksize}"
+ unless grep { $_ == $options->{blocksize} } (1, 2, 4, 8, 16, 32);
+ $options->{segsize} = 1
+ unless $options->{segsize}; # undef or 0 means default
+ # only allow segsize 1 for now, as we can't do large files yet in windows
+ die "Bad segsize $options->{segsize}"
+ unless $options->{segsize} == 1;
+ $options->{wal_blocksize} = 8
+ unless $options->{wal_blocksize}; # undef or 0 means default
+ die "Bad wal_blocksize $options->{wal_blocksize}"
+ unless grep { $_ == $options->{wal_blocksize} }
+ (1, 2, 4, 8, 16, 32, 64);
+
+ return $self;
+}
+
+sub GetAdditionalHeaders
+{
+ return '';
+}
+
+sub DeterminePlatform
+{
+ my $self = shift;
+
+ if ($^O eq "MSWin32")
+ {
+ # Examine CL help output to determine if we are in 32 or 64-bit mode.
+ my $output = `cl /help 2>&1`;
+ $? >> 8 == 0 or die "cl command not found";
+ $self->{platform} =
+ ($output =~ /^\/favor:<.+AMD64/m) ? 'x64' : 'Win32';
+ }
+ else
+ {
+ $self->{platform} = 'FAKE';
+ }
+ print "Detected hardware platform: $self->{platform}\n";
+ return;
+}
+
+# Return 1 if $oldfile is newer than $newfile, or if $newfile doesn't exist.
+# Special case - if config.pl has changed, always return 1
+sub IsNewer
+{
+ my ($newfile, $oldfile) = @_;
+ -e $oldfile or warn "source file \"$oldfile\" does not exist";
+ if ( $oldfile ne 'src/tools/msvc/config.pl'
+ && $oldfile ne 'src/tools/msvc/config_default.pl')
+ {
+ return 1
+ if (-f 'src/tools/msvc/config.pl')
+ && IsNewer($newfile, 'src/tools/msvc/config.pl');
+ return 1
+ if (-f 'src/tools/msvc/config_default.pl')
+ && IsNewer($newfile, 'src/tools/msvc/config_default.pl');
+ }
+ return 1 if (!(-e $newfile));
+ my @nstat = stat($newfile);
+ my @ostat = stat($oldfile);
+ return 1 if ($nstat[9] < $ostat[9]);
+ return 0;
+}
+
+# Copy a file, *not* preserving date. Only works for text files.
+sub copyFile
+{
+ my ($src, $dest) = @_;
+ open(my $i, '<', $src) || croak "Could not open $src";
+ open(my $o, '>', $dest) || croak "Could not open $dest";
+ while (<$i>)
+ {
+ print $o $_;
+ }
+ close($i);
+ close($o);
+ return;
+}
+
+# Fetch version of OpenSSL based on a parsing of the command shipped with
+# the installer this build is linking to. This returns as result an array
+# made of the three first digits of the OpenSSL version, which is enough
+# to decide which options to apply depending on the version of OpenSSL
+# linking with.
+sub GetOpenSSLVersion
+{
+ my $self = shift;
+
+ # Attempt to get OpenSSL version and location. This assumes that
+ # openssl.exe is in the specified directory.
+ # Quote the .exe name in case it has spaces
+ my $opensslcmd =
+ qq("$self->{options}->{openssl}\\bin\\openssl.exe" version 2>&1);
+ my $sslout = `$opensslcmd`;
+
+ $? >> 8 == 0
+ or croak
+ "Unable to determine OpenSSL version: The openssl.exe command wasn't found.";
+
+ if ($sslout =~ /(\d+)\.(\d+)\.(\d+)(\D)/m)
+ {
+ return ($1, $2, $3);
+ }
+
+ croak
+ "Unable to determine OpenSSL version: The openssl.exe version could not be determined.";
+}
+
+sub GenerateFiles
+{
+ my $self = shift;
+ my $bits = $self->{platform} eq 'Win32' ? 32 : 64;
+ my $ac_init_found = 0;
+ my $package_name;
+ my $package_version;
+ my $package_bugreport;
+ my $package_url;
+ my ($majorver, $minorver);
+ my $ac_define_openssl_api_compat_found = 0;
+ my $openssl_api_compat;
+
+ # Parse configure.ac to get version numbers
+ open(my $c, '<', "configure.ac")
+ || confess("Could not open configure.ac for reading\n");
+ while (<$c>)
+ {
+ if (/^AC_INIT\(\[([^\]]+)\], \[([^\]]+)\], \[([^\]]+)\], \[([^\]]*)\], \[([^\]]+)\]/
+ )
+ {
+ $ac_init_found = 1;
+
+ $package_name = $1;
+ $package_version = $2;
+ $package_bugreport = $3;
+ #$package_tarname = $4;
+ $package_url = $5;
+
+ if ($package_version !~ /^(\d+)(?:\.(\d+))?/)
+ {
+ confess "Bad format of version: $package_version\n";
+ }
+ $majorver = sprintf("%d", $1);
+ $minorver = sprintf("%d", $2 ? $2 : 0);
+ }
+ elsif (/\bAC_DEFINE\(OPENSSL_API_COMPAT, \[([0-9xL]+)\]/)
+ {
+ $ac_define_openssl_api_compat_found = 1;
+ $openssl_api_compat = $1;
+ }
+ }
+ close($c);
+ confess "Unable to parse configure.ac for all variables!"
+ unless $ac_init_found && $ac_define_openssl_api_compat_found;
+
+ if (IsNewer("src/include/pg_config_os.h", "src/include/port/win32.h"))
+ {
+ print "Copying pg_config_os.h...\n";
+ copyFile("src/include/port/win32.h", "src/include/pg_config_os.h");
+ }
+
+ print "Generating configuration headers...\n";
+ my $extraver = $self->{options}->{extraver};
+ $extraver = '' unless defined $extraver;
+ my $port = $self->{options}->{"--with-pgport"} || 5432;
+
+ # Every symbol in pg_config.h.in must be accounted for here. Set
+ # to undef if the symbol should not be defined.
+ my %define = (
+ ALIGNOF_DOUBLE => 8,
+ ALIGNOF_INT => 4,
+ ALIGNOF_LONG => 4,
+ ALIGNOF_LONG_LONG_INT => 8,
+ ALIGNOF_PG_INT128_TYPE => undef,
+ ALIGNOF_SHORT => 2,
+ AC_APPLE_UNIVERSAL_BUILD => undef,
+ BLCKSZ => 1024 * $self->{options}->{blocksize},
+ CONFIGURE_ARGS => '"' . $self->GetFakeConfigure() . '"',
+ DEF_PGPORT => $port,
+ DEF_PGPORT_STR => qq{"$port"},
+ DLSUFFIX => '".dll"',
+ ENABLE_GSS => $self->{options}->{gss} ? 1 : undef,
+ ENABLE_NLS => $self->{options}->{nls} ? 1 : undef,
+ ENABLE_THREAD_SAFETY => 1,
+ GETTIMEOFDAY_1ARG => undef,
+ HAVE_APPEND_HISTORY => undef,
+ HAVE_ASN1_STRING_GET0_DATA => undef,
+ HAVE_ATOMICS => 1,
+ HAVE_ATOMIC_H => undef,
+ HAVE_BACKTRACE_SYMBOLS => undef,
+ HAVE_BIO_GET_DATA => undef,
+ HAVE_BIO_METH_NEW => undef,
+ HAVE_CLOCK_GETTIME => undef,
+ HAVE_COMPUTED_GOTO => undef,
+ HAVE_COPYFILE => undef,
+ HAVE_COPYFILE_H => undef,
+ HAVE_CRTDEFS_H => undef,
+ HAVE_CRYPTO_LOCK => undef,
+ HAVE_DECL_FDATASYNC => 0,
+ HAVE_DECL_F_FULLFSYNC => 0,
+ HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER => 0,
+ HAVE_DECL_LLVMCREATEPERFJITEVENTLISTENER => 0,
+ HAVE_DECL_LLVMGETHOSTCPUNAME => 0,
+ HAVE_DECL_LLVMGETHOSTCPUFEATURES => 0,
+ HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN => 0,
+ HAVE_DECL_POSIX_FADVISE => 0,
+ HAVE_DECL_PREADV => 0,
+ HAVE_DECL_PWRITEV => 0,
+ HAVE_DECL_RTLD_GLOBAL => 0,
+ HAVE_DECL_RTLD_NOW => 0,
+ HAVE_DECL_SIGWAIT => 0,
+ HAVE_DECL_STRLCAT => 0,
+ HAVE_DECL_STRLCPY => 0,
+ HAVE_DECL_STRNLEN => 1,
+ HAVE_DECL_STRTOLL => 1,
+ HAVE_DECL_STRTOULL => 1,
+ HAVE_DLOPEN => undef,
+ HAVE_EDITLINE_HISTORY_H => undef,
+ HAVE_EDITLINE_READLINE_H => undef,
+ HAVE_EXECINFO_H => undef,
+ HAVE_EXPLICIT_BZERO => undef,
+ HAVE_FDATASYNC => undef,
+ HAVE_FLS => undef,
+ HAVE_FSEEKO => 1,
+ HAVE_FUNCNAME__FUNC => undef,
+ HAVE_FUNCNAME__FUNCTION => 1,
+ HAVE_GCC__ATOMIC_INT32_CAS => undef,
+ HAVE_GCC__ATOMIC_INT64_CAS => undef,
+ HAVE_GCC__SYNC_CHAR_TAS => undef,
+ HAVE_GCC__SYNC_INT32_CAS => undef,
+ HAVE_GCC__SYNC_INT32_TAS => undef,
+ HAVE_GCC__SYNC_INT64_CAS => undef,
+ HAVE_GETADDRINFO => undef,
+ HAVE_GETHOSTBYNAME_R => undef,
+ HAVE_GETIFADDRS => undef,
+ HAVE_GETOPT => undef,
+ HAVE_GETOPT_H => undef,
+ HAVE_GETOPT_LONG => undef,
+ HAVE_GETPEEREID => undef,
+ HAVE_GETPEERUCRED => undef,
+ HAVE_GETPWUID_R => undef,
+ HAVE_GETRLIMIT => undef,
+ HAVE_GETRUSAGE => undef,
+ HAVE_GETTIMEOFDAY => undef,
+ HAVE_GSSAPI_GSSAPI_H => undef,
+ HAVE_GSSAPI_H => undef,
+ HAVE_HMAC_CTX_FREE => undef,
+ HAVE_HMAC_CTX_NEW => undef,
+ HAVE_HISTORY_H => undef,
+ HAVE_HISTORY_TRUNCATE_FILE => undef,
+ HAVE_IFADDRS_H => undef,
+ HAVE_INET_ATON => undef,
+ HAVE_INET_PTON => 1,
+ HAVE_INT_TIMEZONE => 1,
+ HAVE_INT64 => undef,
+ HAVE_INT8 => undef,
+ HAVE_INTTYPES_H => undef,
+ HAVE_INT_OPTERR => undef,
+ HAVE_INT_OPTRESET => undef,
+ HAVE_IPV6 => 1,
+ HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P => undef,
+ HAVE_KQUEUE => undef,
+ HAVE_LANGINFO_H => undef,
+ HAVE_LDAP_H => undef,
+ HAVE_LDAP_INITIALIZE => undef,
+ HAVE_LIBCRYPTO => undef,
+ HAVE_LIBLDAP => undef,
+ HAVE_LIBLZ4 => undef,
+ HAVE_LIBM => undef,
+ HAVE_LIBPAM => undef,
+ HAVE_LIBREADLINE => undef,
+ HAVE_LIBSELINUX => undef,
+ HAVE_LIBSSL => undef,
+ HAVE_LIBWLDAP32 => undef,
+ HAVE_LIBXML2 => undef,
+ HAVE_LIBXSLT => undef,
+ HAVE_LIBZ => $self->{options}->{zlib} ? 1 : undef,
+ HAVE_LIBZSTD => undef,
+ HAVE_LINK => undef,
+ HAVE_LOCALE_T => 1,
+ HAVE_LONG_INT_64 => undef,
+ HAVE_LONG_LONG_INT_64 => 1,
+ HAVE_MBARRIER_H => undef,
+ HAVE_MBSTOWCS_L => 1,
+ HAVE_MEMORY_H => 1,
+ HAVE_MEMSET_S => undef,
+ HAVE_MINIDUMP_TYPE => 1,
+ HAVE_MKDTEMP => undef,
+ HAVE_NETINET_TCP_H => undef,
+ HAVE_NET_IF_H => undef,
+ HAVE_OPENSSL_INIT_SSL => undef,
+ HAVE_OSSP_UUID_H => undef,
+ HAVE_PAM_PAM_APPL_H => undef,
+ HAVE_POLL => undef,
+ HAVE_POLL_H => undef,
+ HAVE_POSIX_DECL_SIGWAIT => undef,
+ HAVE_POSIX_FADVISE => undef,
+ HAVE_POSIX_FALLOCATE => undef,
+ HAVE_PPC_LWARX_MUTEX_HINT => undef,
+ HAVE_PPOLL => undef,
+ HAVE_PREAD => undef,
+ HAVE_PSTAT => undef,
+ HAVE_PS_STRINGS => undef,
+ HAVE_PTHREAD => undef,
+ HAVE_PTHREAD_BARRIER_WAIT => undef,
+ HAVE_PTHREAD_IS_THREADED_NP => undef,
+ HAVE_PTHREAD_PRIO_INHERIT => undef,
+ HAVE_PWRITE => undef,
+ HAVE_READLINE_H => undef,
+ HAVE_READLINE_HISTORY_H => undef,
+ HAVE_READLINE_READLINE_H => undef,
+ HAVE_READLINK => undef,
+ HAVE_READV => undef,
+ HAVE_RL_COMPLETION_MATCHES => undef,
+ HAVE_RL_COMPLETION_SUPPRESS_QUOTE => undef,
+ HAVE_RL_FILENAME_COMPLETION_FUNCTION => undef,
+ HAVE_RL_FILENAME_QUOTE_CHARACTERS => undef,
+ HAVE_RL_FILENAME_QUOTING_FUNCTION => undef,
+ HAVE_RL_RESET_SCREEN_SIZE => undef,
+ HAVE_RL_VARIABLE_BIND => undef,
+ HAVE_SECURITY_PAM_APPL_H => undef,
+ HAVE_SETENV => undef,
+ HAVE_SETPROCTITLE => undef,
+ HAVE_SETPROCTITLE_FAST => undef,
+ HAVE_SETSID => undef,
+ HAVE_SHM_OPEN => undef,
+ HAVE_SOCKLEN_T => 1,
+ HAVE_SPINLOCKS => 1,
+ HAVE_STDBOOL_H => 1,
+ HAVE_STDINT_H => 1,
+ HAVE_STDLIB_H => 1,
+ HAVE_STRCHRNUL => undef,
+ HAVE_STRERROR_R => undef,
+ HAVE_STRINGS_H => undef,
+ HAVE_STRING_H => 1,
+ HAVE_STRLCAT => undef,
+ HAVE_STRLCPY => undef,
+ HAVE_STRNLEN => 1,
+ HAVE_STRSIGNAL => undef,
+ HAVE_STRTOF => 1,
+ HAVE_STRTOLL => 1,
+ HAVE_STRTOQ => undef,
+ HAVE_STRTOULL => 1,
+ HAVE_STRTOUQ => undef,
+ HAVE_STRUCT_ADDRINFO => 1,
+ HAVE_STRUCT_CMSGCRED => undef,
+ HAVE_STRUCT_OPTION => undef,
+ HAVE_STRUCT_SOCKADDR_SA_LEN => undef,
+ HAVE_STRUCT_SOCKADDR_STORAGE => 1,
+ HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY => 1,
+ HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN => undef,
+ HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY => undef,
+ HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN => undef,
+ HAVE_STRUCT_SOCKADDR_UN => undef,
+ HAVE_STRUCT_TM_TM_ZONE => undef,
+ HAVE_SYNC_FILE_RANGE => undef,
+ HAVE_SYMLINK => 1,
+ HAVE_SYNCFS => undef,
+ HAVE_SYSLOG => undef,
+ HAVE_SYS_EPOLL_H => undef,
+ HAVE_SYS_EVENT_H => undef,
+ HAVE_SYS_IPC_H => undef,
+ HAVE_SYS_PERSONALITY_H => undef,
+ HAVE_SYS_PRCTL_H => undef,
+ HAVE_SYS_PROCCTL_H => undef,
+ HAVE_SYS_PSTAT_H => undef,
+ HAVE_SYS_RESOURCE_H => undef,
+ HAVE_SYS_SELECT_H => undef,
+ HAVE_SYS_SEM_H => undef,
+ HAVE_SYS_SHM_H => undef,
+ HAVE_SYS_SIGNALFD_H => undef,
+ HAVE_SYS_SOCKIO_H => undef,
+ HAVE_SYS_STAT_H => 1,
+ HAVE_SYS_TAS_H => undef,
+ HAVE_SYS_TYPES_H => 1,
+ HAVE_SYS_UCRED_H => undef,
+ HAVE_SYS_UIO_H => undef,
+ HAVE_SYS_UN_H => undef,
+ HAVE_TERMIOS_H => undef,
+ HAVE_TYPEOF => undef,
+ HAVE_UCRED_H => undef,
+ HAVE_UINT64 => undef,
+ HAVE_UINT8 => undef,
+ HAVE_UNION_SEMUN => undef,
+ HAVE_UNISTD_H => 1,
+ HAVE_UNSETENV => undef,
+ HAVE_USELOCALE => undef,
+ HAVE_UUID_BSD => undef,
+ HAVE_UUID_E2FS => undef,
+ HAVE_UUID_OSSP => undef,
+ HAVE_UUID_H => undef,
+ HAVE_UUID_UUID_H => undef,
+ HAVE_WINLDAP_H => undef,
+ HAVE_WCSTOMBS_L => 1,
+ HAVE_WCTYPE_H => 1,
+ HAVE_WRITEV => undef,
+ HAVE_X509_GET_SIGNATURE_NID => 1,
+ HAVE_X509_GET_SIGNATURE_INFO => undef,
+ HAVE_X86_64_POPCNTQ => undef,
+ HAVE__BOOL => undef,
+ HAVE__BUILTIN_BSWAP16 => undef,
+ HAVE__BUILTIN_BSWAP32 => undef,
+ HAVE__BUILTIN_BSWAP64 => undef,
+ HAVE__BUILTIN_CLZ => undef,
+ HAVE__BUILTIN_CONSTANT_P => undef,
+ HAVE__BUILTIN_CTZ => undef,
+ HAVE__BUILTIN_FRAME_ADDRESS => undef,
+ HAVE__BUILTIN_OP_OVERFLOW => undef,
+ HAVE__BUILTIN_POPCOUNT => undef,
+ HAVE__BUILTIN_TYPES_COMPATIBLE_P => undef,
+ HAVE__BUILTIN_UNREACHABLE => undef,
+ HAVE__CONFIGTHREADLOCALE => 1,
+ HAVE__CPUID => 1,
+ HAVE__GET_CPUID => undef,
+ HAVE__STATIC_ASSERT => undef,
+ HAVE___STRTOLL => undef,
+ HAVE___STRTOULL => undef,
+ INT64_MODIFIER => qq{"ll"},
+ LOCALE_T_IN_XLOCALE => undef,
+ MAXIMUM_ALIGNOF => 8,
+ MEMSET_LOOP_LIMIT => 1024,
+ OPENSSL_API_COMPAT => $openssl_api_compat,
+ PACKAGE_BUGREPORT => qq{"$package_bugreport"},
+ PACKAGE_NAME => qq{"$package_name"},
+ PACKAGE_STRING => qq{"$package_name $package_version"},
+ PACKAGE_TARNAME => lc qq{"$package_name"},
+ PACKAGE_URL => qq{"$package_url"},
+ PACKAGE_VERSION => qq{"$package_version"},
+ PG_INT128_TYPE => undef,
+ PG_INT64_TYPE => 'long long int',
+ PG_KRB_SRVNAM => qq{"postgres"},
+ PG_MAJORVERSION => qq{"$majorver"},
+ PG_MAJORVERSION_NUM => $majorver,
+ PG_MINORVERSION_NUM => $minorver,
+ PG_PRINTF_ATTRIBUTE => undef,
+ PG_USE_STDBOOL => 1,
+ PG_VERSION => qq{"$package_version$extraver"},
+ PG_VERSION_NUM => sprintf("%d%04d", $majorver, $minorver),
+ PG_VERSION_STR =>
+ qq{"PostgreSQL $package_version$extraver, compiled by Visual C++ build " CppAsString2(_MSC_VER) ", $bits-bit"},
+ PROFILE_PID_DIR => undef,
+ PTHREAD_CREATE_JOINABLE => undef,
+ RELSEG_SIZE => (1024 / $self->{options}->{blocksize}) *
+ $self->{options}->{segsize} * 1024,
+ SIZEOF_BOOL => 1,
+ SIZEOF_LONG => 4,
+ SIZEOF_OFF_T => undef,
+ SIZEOF_SIZE_T => $bits / 8,
+ SIZEOF_VOID_P => $bits / 8,
+ STDC_HEADERS => 1,
+ STRERROR_R_INT => undef,
+ USE_ARMV8_CRC32C => undef,
+ USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK => undef,
+ USE_ASSERT_CHECKING => $self->{options}->{asserts} ? 1 : undef,
+ USE_BONJOUR => undef,
+ USE_BSD_AUTH => undef,
+ USE_ICU => $self->{options}->{icu} ? 1 : undef,
+ USE_LIBXML => undef,
+ USE_LIBXSLT => undef,
+ USE_LZ4 => undef,
+ USE_LDAP => $self->{options}->{ldap} ? 1 : undef,
+ USE_LLVM => undef,
+ USE_NAMED_POSIX_SEMAPHORES => undef,
+ USE_OPENSSL => undef,
+ USE_PAM => undef,
+ USE_SLICING_BY_8_CRC32C => undef,
+ USE_SSE42_CRC32C => undef,
+ USE_SSE42_CRC32C_WITH_RUNTIME_CHECK => 1,
+ USE_SYSTEMD => undef,
+ USE_SYSV_SEMAPHORES => undef,
+ USE_SYSV_SHARED_MEMORY => undef,
+ USE_UNNAMED_POSIX_SEMAPHORES => undef,
+ USE_WIN32_SEMAPHORES => 1,
+ USE_WIN32_SHARED_MEMORY => 1,
+ USE_ZSTD => undef,
+ WCSTOMBS_L_IN_XLOCALE => undef,
+ WORDS_BIGENDIAN => undef,
+ XLOG_BLCKSZ => 1024 * $self->{options}->{wal_blocksize},
+ _FILE_OFFSET_BITS => undef,
+ _LARGEFILE_SOURCE => undef,
+ _LARGE_FILES => undef,
+ inline => '__inline',
+ pg_restrict => '__restrict',
+ # not defined, because it'd conflict with __declspec(restrict)
+ restrict => undef,
+ typeof => undef,);
+
+ if ($self->{options}->{uuid})
+ {
+ $define{HAVE_UUID_OSSP} = 1;
+ $define{HAVE_UUID_H} = 1;
+ }
+ if ($self->{options}->{xml})
+ {
+ $define{HAVE_LIBXML2} = 1;
+ $define{USE_LIBXML} = 1;
+ }
+ if ($self->{options}->{xslt})
+ {
+ $define{HAVE_LIBXSLT} = 1;
+ $define{USE_LIBXSLT} = 1;
+ }
+ if ($self->{options}->{lz4})
+ {
+ $define{HAVE_LIBLZ4} = 1;
+ $define{USE_LZ4} = 1;
+ }
+ if ($self->{options}->{zstd})
+ {
+ $define{HAVE_LIBZSTD} = 1;
+ $define{USE_ZSTD} = 1;
+ }
+ if ($self->{options}->{openssl})
+ {
+ $define{USE_OPENSSL} = 1;
+
+ my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion();
+
+ # Symbols needed with OpenSSL 1.1.1 and above.
+ if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0')
+ || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '1'))
+ {
+ $define{HAVE_X509_GET_SIGNATURE_INFO} = 1;
+ }
+
+ # Symbols needed with OpenSSL 1.1.0 and above.
+ if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0')
+ || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0'))
+ {
+ $define{HAVE_ASN1_STRING_GET0_DATA} = 1;
+ $define{HAVE_BIO_GET_DATA} = 1;
+ $define{HAVE_BIO_METH_NEW} = 1;
+ $define{HAVE_HMAC_CTX_FREE} = 1;
+ $define{HAVE_HMAC_CTX_NEW} = 1;
+ $define{HAVE_OPENSSL_INIT_SSL} = 1;
+ }
+ }
+
+ $self->GenerateConfigHeader('src/include/pg_config.h', \%define, 1);
+ $self->GenerateConfigHeader('src/include/pg_config_ext.h', \%define, 0);
+ $self->GenerateConfigHeader('src/interfaces/ecpg/include/ecpg_config.h',
+ \%define, 0);
+
+ $self->GenerateDefFile(
+ "src/interfaces/libpq/libpqdll.def",
+ "src/interfaces/libpq/exports.txt",
+ "LIBPQ");
+ $self->GenerateDefFile(
+ "src/interfaces/ecpg/ecpglib/ecpglib.def",
+ "src/interfaces/ecpg/ecpglib/exports.txt",
+ "LIBECPG");
+ $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");
+
+ chdir('src/backend/utils');
+ my $pg_proc_dat = '../../../src/include/catalog/pg_proc.dat';
+ if ( IsNewer('fmgr-stamp', 'Gen_fmgrtab.pl')
+ || IsNewer('fmgr-stamp', '../catalog/Catalog.pm')
+ || IsNewer('fmgr-stamp', $pg_proc_dat)
+ || IsNewer('fmgr-stamp', '../../../src/include/access/transam.h'))
+ {
+ system(
+ "perl -I ../catalog Gen_fmgrtab.pl --include-path ../../../src/include/ $pg_proc_dat"
+ );
+ open(my $f, '>', 'fmgr-stamp')
+ || confess "Could not touch fmgr-stamp";
+ close($f);
+ }
+ chdir('../../..');
+
+ if (IsNewer(
+ 'src/include/utils/fmgroids.h',
+ 'src/backend/utils/fmgroids.h'))
+ {
+ copyFile('src/backend/utils/fmgroids.h',
+ 'src/include/utils/fmgroids.h');
+ }
+
+ if (IsNewer(
+ 'src/include/utils/fmgrprotos.h',
+ 'src/backend/utils/fmgrprotos.h'))
+ {
+ copyFile(
+ 'src/backend/utils/fmgrprotos.h',
+ 'src/include/utils/fmgrprotos.h');
+ }
+
+ if (IsNewer(
+ 'src/include/storage/lwlocknames.h',
+ 'src/backend/storage/lmgr/lwlocknames.txt'))
+ {
+ print "Generating lwlocknames.c and lwlocknames.h...\n";
+ chdir('src/backend/storage/lmgr');
+ system('perl generate-lwlocknames.pl lwlocknames.txt');
+ chdir('../../../..');
+ }
+ if (IsNewer(
+ 'src/include/storage/lwlocknames.h',
+ 'src/backend/storage/lmgr/lwlocknames.h'))
+ {
+ copyFile(
+ 'src/backend/storage/lmgr/lwlocknames.h',
+ 'src/include/storage/lwlocknames.h');
+ }
+
+ if (IsNewer('src/include/utils/probes.h', 'src/backend/utils/probes.d'))
+ {
+ print "Generating probes.h...\n";
+ system(
+ 'perl src/backend/utils/Gen_dummy_probes.pl src/backend/utils/probes.d > src/include/utils/probes.h'
+ );
+ }
+
+ if ($self->{options}->{python}
+ && IsNewer(
+ 'src/pl/plpython/spiexceptions.h',
+ 'src/backend/utils/errcodes.txt'))
+ {
+ print "Generating spiexceptions.h...\n";
+ system(
+ 'perl src/pl/plpython/generate-spiexceptions.pl src/backend/utils/errcodes.txt > src/pl/plpython/spiexceptions.h'
+ );
+ }
+
+ if (IsNewer(
+ 'src/include/utils/errcodes.h',
+ 'src/backend/utils/errcodes.txt'))
+ {
+ print "Generating errcodes.h...\n";
+ system(
+ 'perl src/backend/utils/generate-errcodes.pl src/backend/utils/errcodes.txt > src/backend/utils/errcodes.h'
+ );
+ copyFile('src/backend/utils/errcodes.h',
+ 'src/include/utils/errcodes.h');
+ }
+
+ if (IsNewer(
+ 'src/pl/plpgsql/src/plerrcodes.h',
+ 'src/backend/utils/errcodes.txt'))
+ {
+ print "Generating plerrcodes.h...\n";
+ system(
+ 'perl src/pl/plpgsql/src/generate-plerrcodes.pl src/backend/utils/errcodes.txt > src/pl/plpgsql/src/plerrcodes.h'
+ );
+ }
+
+ if ($self->{options}->{tcl}
+ && IsNewer(
+ 'src/pl/tcl/pltclerrcodes.h', 'src/backend/utils/errcodes.txt'))
+ {
+ print "Generating pltclerrcodes.h...\n";
+ system(
+ 'perl src/pl/tcl/generate-pltclerrcodes.pl src/backend/utils/errcodes.txt > src/pl/tcl/pltclerrcodes.h'
+ );
+ }
+
+ if (IsNewer('src/bin/psql/sql_help.h', 'src/bin/psql/create_help.pl'))
+ {
+ print "Generating sql_help.h...\n";
+ chdir('src/bin/psql');
+ system("perl create_help.pl ../../../doc/src/sgml/ref sql_help");
+ chdir('../../..');
+ }
+
+ if (IsNewer('src/common/kwlist_d.h', 'src/include/parser/kwlist.h'))
+ {
+ print "Generating kwlist_d.h...\n";
+ system(
+ 'perl -I src/tools src/tools/gen_keywordlist.pl --extern -o src/common src/include/parser/kwlist.h'
+ );
+ }
+
+ if (IsNewer(
+ 'src/pl/plpgsql/src/pl_reserved_kwlist_d.h',
+ 'src/pl/plpgsql/src/pl_reserved_kwlist.h')
+ || IsNewer(
+ 'src/pl/plpgsql/src/pl_unreserved_kwlist_d.h',
+ 'src/pl/plpgsql/src/pl_unreserved_kwlist.h'))
+ {
+ print
+ "Generating pl_reserved_kwlist_d.h and pl_unreserved_kwlist_d.h...\n";
+ chdir('src/pl/plpgsql/src');
+ system(
+ 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ReservedPLKeywords pl_reserved_kwlist.h'
+ );
+ system(
+ 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname UnreservedPLKeywords pl_unreserved_kwlist.h'
+ );
+ chdir('../../../..');
+ }
+
+ if (IsNewer(
+ 'src/interfaces/ecpg/preproc/c_kwlist_d.h',
+ 'src/interfaces/ecpg/preproc/c_kwlist.h')
+ || IsNewer(
+ 'src/interfaces/ecpg/preproc/ecpg_kwlist_d.h',
+ 'src/interfaces/ecpg/preproc/ecpg_kwlist.h'))
+ {
+ print "Generating c_kwlist_d.h and ecpg_kwlist_d.h...\n";
+ chdir('src/interfaces/ecpg/preproc');
+ system(
+ 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ScanCKeywords --no-case-fold c_kwlist.h'
+ );
+ system(
+ 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ScanECPGKeywords ecpg_kwlist.h'
+ );
+ chdir('../../../..');
+ }
+
+ if (IsNewer(
+ 'src/interfaces/ecpg/preproc/preproc.y',
+ 'src/backend/parser/gram.y'))
+ {
+ print "Generating preproc.y...\n";
+ chdir('src/interfaces/ecpg/preproc');
+ system('perl parse.pl < ../../../backend/parser/gram.y > preproc.y');
+ chdir('../../../..');
+ }
+
+ unless (-f "src/port/pg_config_paths.h")
+ {
+ print "Generating pg_config_paths.h...\n";
+ open(my $o, '>', 'src/port/pg_config_paths.h')
+ || confess "Could not open pg_config_paths.h";
+ print $o <<EOF;
+#define PGBINDIR "/bin"
+#define PGSHAREDIR "/share"
+#define SYSCONFDIR "/etc"
+#define INCLUDEDIR "/include"
+#define PKGINCLUDEDIR "/include"
+#define INCLUDEDIRSERVER "/include/server"
+#define LIBDIR "/lib"
+#define PKGLIBDIR "/lib"
+#define LOCALEDIR "/share/locale"
+#define DOCDIR "/doc"
+#define HTMLDIR "/doc"
+#define MANDIR "/man"
+EOF
+ close($o);
+ }
+
+ my $mf = Project::read_file('src/backend/catalog/Makefile');
+ $mf =~ s{\\\r?\n}{}g;
+ $mf =~ /^CATALOG_HEADERS\s*:?=(.*)$/gm
+ || croak "Could not find CATALOG_HEADERS in Makefile\n";
+ my @bki_srcs = split /\s+/, $1;
+ $mf =~ /^POSTGRES_BKI_DATA\s*:?=[^,]+,(.*)\)$/gm
+ || croak "Could not find POSTGRES_BKI_DATA in Makefile\n";
+ my @bki_data = split /\s+/, $1;
+
+ my $need_genbki = 0;
+ foreach my $bki (@bki_srcs, @bki_data)
+ {
+ next if $bki eq "";
+ if (IsNewer(
+ 'src/backend/catalog/bki-stamp',
+ "src/include/catalog/$bki"))
+ {
+ $need_genbki = 1;
+ last;
+ }
+ }
+ $need_genbki = 1
+ if IsNewer('src/backend/catalog/bki-stamp',
+ 'src/backend/catalog/genbki.pl');
+ $need_genbki = 1
+ if IsNewer('src/backend/catalog/bki-stamp',
+ 'src/backend/catalog/Catalog.pm');
+ if ($need_genbki)
+ {
+ chdir('src/backend/catalog');
+ my $bki_srcs = join(' ../../../src/include/catalog/', @bki_srcs);
+ system(
+ "perl genbki.pl --include-path ../../../src/include/ --set-version=$majorver $bki_srcs"
+ );
+ open(my $f, '>', 'bki-stamp')
+ || confess "Could not touch bki-stamp";
+ close($f);
+ chdir('../../..');
+ }
+
+ if (IsNewer(
+ 'src/include/catalog/header-stamp',
+ 'src/backend/catalog/bki-stamp'))
+ {
+ # Copy generated headers to include directory.
+ opendir(my $dh, 'src/backend/catalog/')
+ || die "Can't opendir src/backend/catalog/ $!";
+ my @def_headers = grep { /pg_\w+_d\.h$/ } readdir($dh);
+ closedir $dh;
+ foreach my $def_header (@def_headers)
+ {
+ copyFile(
+ "src/backend/catalog/$def_header",
+ "src/include/catalog/$def_header");
+ }
+ copyFile(
+ 'src/backend/catalog/schemapg.h',
+ 'src/include/catalog/schemapg.h');
+ copyFile(
+ 'src/backend/catalog/system_fk_info.h',
+ 'src/include/catalog/system_fk_info.h');
+ open(my $chs, '>', 'src/include/catalog/header-stamp')
+ || confess "Could not touch header-stamp";
+ close($chs);
+ }
+
+ open(my $o, '>', "doc/src/sgml/version.sgml")
+ || croak "Could not write to version.sgml\n";
+ print $o <<EOF;
+<!ENTITY version "$package_version">
+<!ENTITY majorversion "$majorver">
+EOF
+ close($o);
+ return;
+}
+
+# Read lines from input file and substitute symbols using the same
+# logic that config.status uses. There should be one call of this for
+# each AC_CONFIG_HEADERS call in configure.ac.
+#
+# If the "required" argument is true, we also keep track which of our
+# defines have been found and error out if any are left unused at the
+# end. That way we avoid accumulating defines in this file that are
+# no longer used by configure.
+sub GenerateConfigHeader
+{
+ my ($self, $config_header, $defines, $required) = @_;
+
+ my $config_header_in = $config_header . '.in';
+
+ if ( IsNewer($config_header, $config_header_in)
+ || IsNewer($config_header, __FILE__))
+ {
+ my %defines_copy = %$defines;
+
+ open(my $i, '<', $config_header_in)
+ || confess "Could not open $config_header_in\n";
+ open(my $o, '>', $config_header)
+ || confess "Could not write to $config_header\n";
+
+ print $o
+ "/* $config_header. Generated from $config_header_in by src/tools/msvc/Solution.pm. */\n";
+
+ while (<$i>)
+ {
+ if (m/^#(\s*)undef\s+(\w+)/)
+ {
+ my $ws = $1;
+ my $macro = $2;
+ if (exists $defines->{$macro})
+ {
+ if (defined $defines->{$macro})
+ {
+ print $o "#${ws}define $macro ", $defines->{$macro},
+ "\n";
+ }
+ else
+ {
+ print $o "/* #${ws}undef $macro */\n";
+ }
+ delete $defines_copy{$macro};
+ }
+ else
+ {
+ croak
+ "undefined symbol: $macro at $config_header line $.";
+ }
+ }
+ else
+ {
+ print $o $_;
+ }
+ }
+ close($o);
+ close($i);
+
+ if ($required && scalar(keys %defines_copy) > 0)
+ {
+ croak "unused defines: " . join(' ', keys %defines_copy);
+ }
+ }
+}
+
+sub GenerateDefFile
+{
+ my ($self, $deffile, $txtfile, $libname) = @_;
+
+ if (IsNewer($deffile, $txtfile))
+ {
+ print "Generating $deffile...\n";
+ open(my $if, '<', $txtfile) || confess("Could not open $txtfile\n");
+ open(my $of, '>', $deffile) || confess("Could not open $deffile\n");
+ print $of "LIBRARY $libname\nEXPORTS\n";
+ while (<$if>)
+ {
+ next if (/^#/);
+ next if (/^\s*$/);
+ my ($f, $o) = split;
+ print $of " $f @ $o\n";
+ }
+ close($of);
+ close($if);
+ }
+ return;
+}
+
+sub AddProject
+{
+ my ($self, $name, $type, $folder, $initialdir) = @_;
+
+ my $proj =
+ VSObjectFactory::CreateProject($self->{vcver}, $name, $type, $self);
+ push @{ $self->{projects}->{$folder} }, $proj;
+ $proj->AddDir($initialdir) if ($initialdir);
+ if ($self->{options}->{zlib})
+ {
+ $proj->AddIncludeDir($self->{options}->{zlib} . '\include');
+ $proj->AddLibrary($self->{options}->{zlib} . '\lib\zdll.lib');
+ }
+ if ($self->{options}->{openssl})
+ {
+ $proj->AddIncludeDir($self->{options}->{openssl} . '\include');
+ my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion();
+
+ # Starting at version 1.1.0 the OpenSSL installers have
+ # changed their library names from:
+ # - libeay to libcrypto
+ # - ssleay to libssl
+ if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0')
+ || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0'))
+ {
+ my $dbgsuffix;
+ my $libsslpath;
+ my $libcryptopath;
+
+ # The format name of the libraries is slightly
+ # different between the Win32 and Win64 platform, so
+ # adapt.
+ if (-e "$self->{options}->{openssl}/lib/VC/sslcrypto32MD.lib")
+ {
+ # Win32 here, with a debugging library set.
+ $dbgsuffix = 1;
+ $libsslpath = '\lib\VC\libssl32.lib';
+ $libcryptopath = '\lib\VC\libcrypto32.lib';
+ }
+ elsif (-e "$self->{options}->{openssl}/lib/VC/sslcrypto64MD.lib")
+ {
+ # Win64 here, with a debugging library set.
+ $dbgsuffix = 1;
+ $libsslpath = '\lib\VC\libssl64.lib';
+ $libcryptopath = '\lib\VC\libcrypto64.lib';
+ }
+ else
+ {
+ # On both Win32 and Win64 the same library
+ # names are used without a debugging context.
+ $dbgsuffix = 0;
+ $libsslpath = '\lib\libssl.lib';
+ $libcryptopath = '\lib\libcrypto.lib';
+ }
+
+ $proj->AddLibrary($self->{options}->{openssl} . $libsslpath,
+ $dbgsuffix);
+ $proj->AddLibrary($self->{options}->{openssl} . $libcryptopath,
+ $dbgsuffix);
+ }
+ else
+ {
+ # Choose which set of libraries to use depending on if
+ # debugging libraries are in place in the installer.
+ if (-e "$self->{options}->{openssl}/lib/VC/ssleay32MD.lib")
+ {
+ $proj->AddLibrary(
+ $self->{options}->{openssl} . '\lib\VC\ssleay32.lib', 1);
+ $proj->AddLibrary(
+ $self->{options}->{openssl} . '\lib\VC\libeay32.lib', 1);
+ }
+ else
+ {
+ # We don't expect the config-specific library
+ # to be here, so don't ask for it in last
+ # parameter.
+ $proj->AddLibrary(
+ $self->{options}->{openssl} . '\lib\ssleay32.lib', 0);
+ $proj->AddLibrary(
+ $self->{options}->{openssl} . '\lib\libeay32.lib', 0);
+ }
+ }
+ }
+ if ($self->{options}->{nls})
+ {
+ $proj->AddIncludeDir($self->{options}->{nls} . '\include');
+ $proj->AddLibrary($self->{options}->{nls} . '\lib\libintl.lib');
+ }
+ if ($self->{options}->{gss})
+ {
+ $proj->AddIncludeDir($self->{options}->{gss} . '\include');
+ $proj->AddIncludeDir($self->{options}->{gss} . '\include\krb5');
+ if ($self->{platform} eq 'Win32')
+ {
+ $proj->AddLibrary(
+ $self->{options}->{gss} . '\lib\i386\krb5_32.lib');
+ $proj->AddLibrary(
+ $self->{options}->{gss} . '\lib\i386\comerr32.lib');
+ $proj->AddLibrary(
+ $self->{options}->{gss} . '\lib\i386\gssapi32.lib');
+ }
+ else
+ {
+ $proj->AddLibrary(
+ $self->{options}->{gss} . '\lib\amd64\krb5_64.lib');
+ $proj->AddLibrary(
+ $self->{options}->{gss} . '\lib\amd64\comerr64.lib');
+ $proj->AddLibrary(
+ $self->{options}->{gss} . '\lib\amd64\gssapi64.lib');
+ }
+ }
+ if ($self->{options}->{iconv})
+ {
+ $proj->AddIncludeDir($self->{options}->{iconv} . '\include');
+ $proj->AddLibrary($self->{options}->{iconv} . '\lib\iconv.lib');
+ }
+ if ($self->{options}->{icu})
+ {
+ $proj->AddIncludeDir($self->{options}->{icu} . '\include');
+ if ($self->{platform} eq 'Win32')
+ {
+ $proj->AddLibrary($self->{options}->{icu} . '\lib\icuin.lib');
+ $proj->AddLibrary($self->{options}->{icu} . '\lib\icuuc.lib');
+ $proj->AddLibrary($self->{options}->{icu} . '\lib\icudt.lib');
+ }
+ else
+ {
+ $proj->AddLibrary($self->{options}->{icu} . '\lib64\icuin.lib');
+ $proj->AddLibrary($self->{options}->{icu} . '\lib64\icuuc.lib');
+ $proj->AddLibrary($self->{options}->{icu} . '\lib64\icudt.lib');
+ }
+ }
+ if ($self->{options}->{xml})
+ {
+ $proj->AddIncludeDir($self->{options}->{xml} . '\include');
+ $proj->AddIncludeDir($self->{options}->{xml} . '\include\libxml2');
+ $proj->AddLibrary($self->{options}->{xml} . '\lib\libxml2.lib');
+ }
+ if ($self->{options}->{xslt})
+ {
+ $proj->AddIncludeDir($self->{options}->{xslt} . '\include');
+ $proj->AddLibrary($self->{options}->{xslt} . '\lib\libxslt.lib');
+ }
+ if ($self->{options}->{lz4})
+ {
+ $proj->AddIncludeDir($self->{options}->{lz4} . '\include');
+ $proj->AddLibrary($self->{options}->{lz4} . '\lib\liblz4.lib');
+ }
+ if ($self->{options}->{zstd})
+ {
+ $proj->AddIncludeDir($self->{options}->{zstd} . '\include');
+ $proj->AddLibrary($self->{options}->{zstd} . '\lib\libzstd.lib');
+ }
+ if ($self->{options}->{uuid})
+ {
+ $proj->AddIncludeDir($self->{options}->{uuid} . '\include');
+ $proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib');
+ }
+ return $proj;
+}
+
+sub Save
+{
+ my ($self) = @_;
+ my %flduid;
+
+ $self->GenerateFiles();
+ foreach my $fld (keys %{ $self->{projects} })
+ {
+ foreach my $proj (@{ $self->{projects}->{$fld} })
+ {
+ $proj->Save();
+ }
+ }
+
+ open(my $sln, '>', "pgsql.sln") || croak "Could not write to pgsql.sln\n";
+ print $sln <<EOF;
+Microsoft Visual Studio Solution File, Format Version $self->{solutionFileVersion}
+# $self->{visualStudioName}
+EOF
+
+ print $sln $self->GetAdditionalHeaders();
+
+ foreach my $fld (keys %{ $self->{projects} })
+ {
+ foreach my $proj (@{ $self->{projects}->{$fld} })
+ {
+ print $sln <<EOF;
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "$proj->{name}", "$proj->{name}$proj->{filenameExtension}", "$proj->{guid}"
+EndProject
+EOF
+ }
+ if ($fld ne "")
+ {
+ $flduid{$fld} = $^O eq "MSWin32" ? Win32::GuidGen() : 'FAKE';
+ print $sln <<EOF;
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "$fld", "$fld", "$flduid{$fld}"
+EndProject
+EOF
+ }
+ }
+
+ print $sln <<EOF;
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|$self->{platform}= Debug|$self->{platform}
+ Release|$self->{platform} = Release|$self->{platform}
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+EOF
+
+ foreach my $fld (keys %{ $self->{projects} })
+ {
+ foreach my $proj (@{ $self->{projects}->{$fld} })
+ {
+ print $sln <<EOF;
+ $proj->{guid}.Debug|$self->{platform}.ActiveCfg = Debug|$self->{platform}
+ $proj->{guid}.Debug|$self->{platform}.Build.0 = Debug|$self->{platform}
+ $proj->{guid}.Release|$self->{platform}.ActiveCfg = Release|$self->{platform}
+ $proj->{guid}.Release|$self->{platform}.Build.0 = Release|$self->{platform}
+EOF
+ }
+ }
+
+ print $sln <<EOF;
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+EOF
+
+ foreach my $fld (keys %{ $self->{projects} })
+ {
+ next if ($fld eq "");
+ foreach my $proj (@{ $self->{projects}->{$fld} })
+ {
+ print $sln "\t\t$proj->{guid} = $flduid{$fld}\n";
+ }
+ }
+
+ print $sln <<EOF;
+ EndGlobalSection
+EndGlobal
+EOF
+ close($sln);
+ return;
+}
+
+sub GetFakeConfigure
+{
+ my $self = shift;
+
+ my $cfg = '--enable-thread-safety';
+ $cfg .= ' --enable-cassert' if ($self->{options}->{asserts});
+ $cfg .= ' --enable-nls' if ($self->{options}->{nls});
+ $cfg .= ' --enable-tap-tests' if ($self->{options}->{tap_tests});
+ $cfg .= ' --with-ldap' if ($self->{options}->{ldap});
+ $cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
+ $cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
+ $cfg .= ' --with-ssl=openssl' if ($self->{options}->{openssl});
+ $cfg .= ' --with-uuid' if ($self->{options}->{uuid});
+ $cfg .= ' --with-libxml' if ($self->{options}->{xml});
+ $cfg .= ' --with-libxslt' if ($self->{options}->{xslt});
+ $cfg .= ' --with-lz4' if ($self->{options}->{lz4});
+ $cfg .= ' --with-zstd' if ($self->{options}->{zstd});
+ $cfg .= ' --with-gssapi' if ($self->{options}->{gss});
+ $cfg .= ' --with-icu' if ($self->{options}->{icu});
+ $cfg .= ' --with-tcl' if ($self->{options}->{tcl});
+ $cfg .= ' --with-perl' if ($self->{options}->{perl});
+ $cfg .= ' --with-python' if ($self->{options}->{python});
+ my $port = $self->{options}->{'--with-pgport'};
+ $cfg .= " --with-pgport=$port" if defined($port);
+
+ return $cfg;
+}
+
+package VS2013Solution;
+
+#
+# Package that encapsulates a Visual Studio 2013 solution file
+#
+
+use Carp;
+use strict;
+use warnings;
+use base qw(Solution);
+
+no warnings qw(redefine); ## no critic
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{solutionFileVersion} = '12.00';
+ $self->{vcver} = '12.00';
+ $self->{visualStudioName} = 'Visual Studio 2013';
+ $self->{VisualStudioVersion} = '12.0.21005.1';
+ $self->{MinimumVisualStudioVersion} = '10.0.40219.1';
+
+ return $self;
+}
+
+package VS2015Solution;
+
+#
+# Package that encapsulates a Visual Studio 2015 solution file
+#
+
+use Carp;
+use strict;
+use warnings;
+use base qw(Solution);
+
+no warnings qw(redefine); ## no critic
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{solutionFileVersion} = '12.00';
+ $self->{vcver} = '14.00';
+ $self->{visualStudioName} = 'Visual Studio 2015';
+ $self->{VisualStudioVersion} = '14.0.24730.2';
+ $self->{MinimumVisualStudioVersion} = '10.0.40219.1';
+
+ return $self;
+}
+
+package VS2017Solution;
+
+#
+# Package that encapsulates a Visual Studio 2017 solution file
+#
+
+use Carp;
+use strict;
+use warnings;
+use base qw(Solution);
+
+no warnings qw(redefine); ## no critic
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{solutionFileVersion} = '12.00';
+ $self->{vcver} = '15.00';
+ $self->{visualStudioName} = 'Visual Studio 2017';
+ $self->{VisualStudioVersion} = '15.0.26730.3';
+ $self->{MinimumVisualStudioVersion} = '10.0.40219.1';
+
+ return $self;
+}
+
+package VS2019Solution;
+
+#
+# Package that encapsulates a Visual Studio 2019 solution file
+#
+
+use Carp;
+use strict;
+use warnings;
+use base qw(Solution);
+
+no warnings qw(redefine); ## no critic
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{solutionFileVersion} = '12.00';
+ $self->{vcver} = '16.00';
+ $self->{visualStudioName} = 'Visual Studio 2019';
+ $self->{VisualStudioVersion} = '16.0.28729.10';
+ $self->{MinimumVisualStudioVersion} = '10.0.40219.1';
+
+ return $self;
+}
+
+package VS2022Solution;
+
+#
+# Package that encapsulates a Visual Studio 2022 solution file
+#
+
+use Carp;
+use strict;
+use warnings;
+use base qw(Solution);
+
+no warnings qw(redefine); ## no critic
+
+sub new
+{
+ my $classname = shift;
+ my $self = $classname->SUPER::_new(@_);
+ bless($self, $classname);
+
+ $self->{solutionFileVersion} = '12.00';
+ $self->{vcver} = '17.00';
+ $self->{visualStudioName} = 'Visual Studio 2022';
+ $self->{VisualStudioVersion} = '17.0.31903.59';
+ $self->{MinimumVisualStudioVersion} = '10.0.40219.1';
+
+ return $self;
+}
+
+sub GetAdditionalHeaders
+{
+ my ($self, $f) = @_;
+
+ return qq|VisualStudioVersion = $self->{VisualStudioVersion}
+MinimumVisualStudioVersion = $self->{MinimumVisualStudioVersion}
+|;
+}
+
+1;
diff --git a/src/tools/msvc/VSObjectFactory.pm b/src/tools/msvc/VSObjectFactory.pm
new file mode 100644
index 0000000..9f9712b
--- /dev/null
+++ b/src/tools/msvc/VSObjectFactory.pm
@@ -0,0 +1,182 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+package VSObjectFactory;
+
+#
+# Package that creates Visual Studio wrapper objects for msvc build
+#
+# src/tools/msvc/VSObjectFactory.pm
+#
+
+use Carp;
+use strict;
+use warnings;
+
+use Exporter;
+use Project;
+use Solution;
+use MSBuildProject;
+
+our (@ISA, @EXPORT);
+@ISA = qw(Exporter);
+@EXPORT = qw(CreateSolution CreateProject DetermineVisualStudioVersion);
+
+no warnings qw(redefine); ## no critic
+
+sub CreateSolution
+{
+ my $visualStudioVersion = shift;
+
+ if (!defined($visualStudioVersion))
+ {
+ $visualStudioVersion = DetermineVisualStudioVersion();
+ }
+
+ if ($visualStudioVersion eq '12.00')
+ {
+ return new VS2013Solution(@_);
+ }
+ elsif ($visualStudioVersion eq '14.00')
+ {
+ return new VS2015Solution(@_);
+ }
+
+ # The version of nmake bundled in Visual Studio 2017 is greater
+ # than 14.10 and less than 14.20. And the version number is
+ # actually 15.00.
+ elsif (
+ ($visualStudioVersion ge '14.10' && $visualStudioVersion lt '14.20')
+ || $visualStudioVersion eq '15.00')
+ {
+ return new VS2017Solution(@_);
+ }
+
+ # The version of nmake bundled in Visual Studio 2019 is greater
+ # than 14.20 and less than 14.30. And the version number is
+ # actually 16.00.
+ elsif (
+ ($visualStudioVersion ge '14.20' && $visualStudioVersion lt '14.30')
+ || $visualStudioVersion eq '16.00')
+ {
+ return new VS2019Solution(@_);
+ }
+
+ # The version of nmake bundled in Visual Studio 2022 is greater
+ # than 14.30 and less than 14.40. And the version number is
+ # actually 17.00.
+ elsif (
+ ($visualStudioVersion ge '14.30' && $visualStudioVersion lt '14.40')
+ || $visualStudioVersion eq '17.00')
+ {
+ return new VS2022Solution(@_);
+ }
+ else
+ {
+ croak
+ "The requested Visual Studio version $visualStudioVersion is not supported.";
+ }
+}
+
+sub CreateProject
+{
+ my $visualStudioVersion = shift;
+
+ if (!defined($visualStudioVersion))
+ {
+ $visualStudioVersion = DetermineVisualStudioVersion();
+ }
+
+ if ($visualStudioVersion eq '12.00')
+ {
+ return new VC2013Project(@_);
+ }
+ elsif ($visualStudioVersion eq '14.00')
+ {
+ return new VC2015Project(@_);
+ }
+
+ # The version of nmake bundled in Visual Studio 2017 is greater
+ # than 14.10 and less than 14.20. And the version number is
+ # actually 15.00.
+ elsif (
+ ($visualStudioVersion ge '14.10' && $visualStudioVersion lt '14.20')
+ || $visualStudioVersion eq '15.00')
+ {
+ return new VC2017Project(@_);
+ }
+
+ # The version of nmake bundled in Visual Studio 2019 is greater
+ # than 14.20 and less than 14.30. And the version number is
+ # actually 16.00.
+ elsif (
+ ($visualStudioVersion ge '14.20' && $visualStudioVersion lt '14.30')
+ || $visualStudioVersion eq '16.00')
+ {
+ return new VC2019Project(@_);
+ }
+
+ # The version of nmake bundled in Visual Studio 2022 is greater
+ # than 14.30 and less than 14.40. And the version number is
+ # actually 17.00.
+ elsif (
+ ($visualStudioVersion ge '14.30' && $visualStudioVersion lt '14.40')
+ || $visualStudioVersion eq '17.00')
+ {
+ return new VC2022Project(@_);
+ }
+ else
+ {
+ croak
+ "The requested Visual Studio version $visualStudioVersion is not supported.";
+ }
+}
+
+sub DetermineVisualStudioVersion
+{
+ if ($^O eq "MSWin32")
+ {
+ # To determine version of Visual Studio we use nmake as it has
+ # existed for a long time and still exists in current Visual
+ # Studio versions.
+ my $output = `nmake /? 2>&1`;
+ $? >> 8 == 0
+ or croak
+ "Unable to determine Visual Studio version: The nmake command wasn't found.";
+ if ($output =~ /(\d+)\.(\d+)\.\d+(\.\d+)?/)
+ {
+ return _GetVisualStudioVersion($1, $2);
+ }
+
+ croak
+ "Unable to determine Visual Studio version: The nmake version could not be determined.";
+ }
+ else
+ {
+ # fake version
+ return '17.00';
+ }
+}
+
+sub _GetVisualStudioVersion
+{
+ my ($major, $minor) = @_;
+
+ # The major visual studio that is supported has nmake
+ # version <= 14.40, so stick with it as the latest version
+ # if bumping on something even newer.
+ if ($major >= 14 && $minor >= 40)
+ {
+ carp
+ "The determined version of Visual Studio is newer than the latest supported version. Returning the latest supported version instead.";
+ return '14.30';
+ }
+ elsif ($major < 12)
+ {
+ croak
+ "Unable to determine Visual Studio version: Visual Studio versions before 12.0 aren't supported.";
+ }
+ return "$major.$minor";
+}
+
+1;
diff --git a/src/tools/msvc/build.bat b/src/tools/msvc/build.bat
new file mode 100755
index 0000000..171f749
--- /dev/null
+++ b/src/tools/msvc/build.bat
@@ -0,0 +1,6 @@
+@echo off
+REM src/tools/msvc/build.bat
+REM all the logic for this now belongs in build.pl. This file really
+REM only exists so you don't have to type "perl build.pl"
+REM Resist any temptation to add any logic here.
+@perl %~dp0/build.pl %*
diff --git a/src/tools/msvc/build.pl b/src/tools/msvc/build.pl
new file mode 100644
index 0000000..e5ce14d
--- /dev/null
+++ b/src/tools/msvc/build.pl
@@ -0,0 +1,92 @@
+# -*-perl-*- hey - emacs - this is a perl file
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+#
+# Script that provides 'make' functionality for msvc builds.
+#
+# src/tools/msvc/build.pl
+#
+use strict;
+use warnings;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use Cwd;
+
+use Mkvcbuild;
+
+sub usage
+{
+ die( "Usage: build.pl [ [ <configuration> ] <component> ]\n"
+ . "Options are case-insensitive.\n"
+ . " configuration: Release | Debug. This sets the configuration\n"
+ . " to build. Default is Release.\n"
+ . " component: name of component to build. An empty value means\n"
+ . " to build all components.\n");
+}
+
+chdir('../../..') if (-d '../msvc' && -d '../../../src');
+die 'Must run from root or msvc directory'
+ unless (-d 'src/tools/msvc' && -d 'src');
+
+usage() unless scalar(@ARGV) <= 2;
+
+# buildenv.pl is for specifying the build environment settings
+# it should contain lines like:
+# $ENV{PATH} = "c:/path/to/bison/bin;$ENV{PATH}";
+
+if (-e "src/tools/msvc/buildenv.pl")
+{
+ do "./src/tools/msvc/buildenv.pl";
+}
+elsif (-e "./buildenv.pl")
+{
+ do "./buildenv.pl";
+}
+
+# set up the project
+our $config;
+do "./src/tools/msvc/config_default.pl";
+do "./src/tools/msvc/config.pl" if (-f "src/tools/msvc/config.pl");
+
+my $vcver = Mkvcbuild::mkvcbuild($config);
+
+# check what sort of build we are doing
+my $bconf = $ENV{CONFIG} || "Release";
+my $msbflags = $ENV{MSBFLAGS} || "";
+my $buildwhat = $ARGV[1] || "";
+
+if (defined($ARGV[0]))
+{
+ if (uc($ARGV[0]) eq 'DEBUG')
+ {
+ $bconf = "Debug";
+ }
+ elsif (uc($ARGV[0]) ne "RELEASE")
+ {
+ $buildwhat = $ARGV[0] || "";
+ }
+}
+
+# ... and do it
+
+if ($buildwhat)
+{
+ system(
+ "msbuild $buildwhat.vcxproj /verbosity:normal $msbflags /p:Configuration=$bconf"
+ );
+}
+else
+{
+ system(
+ "msbuild pgsql.sln /verbosity:normal $msbflags /p:Configuration=$bconf"
+ );
+}
+
+# report status
+
+my $status = $? >> 8;
+
+exit $status;
diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat
new file mode 100755
index 0000000..42b3d1b
--- /dev/null
+++ b/src/tools/msvc/clean.bat
@@ -0,0 +1,142 @@
+@echo off
+REM src/tools/msvc/clean.bat
+
+set DIST=0
+if "%1"=="dist" set DIST=1
+
+setlocal
+
+cd "%~dp0\..\..\.."
+
+if exist debug rd /s /q debug
+if exist release rd /s /q release
+for %%f in (*.vcproj) do del %%f
+for %%f in (*.vcxproj) do del %%f
+for %%f in (*.vcxproj.user) do del %%f
+if exist pgsql.sln del /q pgsql.sln
+if exist pgsql.sln.cache del /q pgsql.sln.cache
+if exist pgsql.sdf del /q pgsql.sdf
+if exist pgsql.suo del /q /a:H pgsql.suo
+del /s /q src\bin\win32ver.rc 2> NUL
+del /s /q src\interfaces\win32ver.rc 2> NUL
+if exist src\backend\win32ver.rc del /q src\backend\win32ver.rc
+if exist src\backend\replication\libpqwalreceiver\win32ver.rc del /q src\backend\replication\libpqwalreceiver\win32ver.rc
+if exist src\backend\replication\pgoutput\win32ver.rc del /q src\backend\replication\pgoutput\win32ver.rc
+if exist src\backend\snowball\win32ver.rc del /q src\backend\snowball\win32ver.rc
+if exist src\interfaces\ecpg\test\win32ver.rc del /q src\interfaces\ecpg\test\win32ver.rc
+if exist src\pl\plperl\win32ver.rc del /q src\pl\plperl\win32ver.rc
+if exist src\pl\plpgsql\src\win32ver.rc del /q src\pl\plpgsql\src\win32ver.rc
+if exist src\pl\plpython\win32ver.rc del /q src\pl\plpython\win32ver.rc
+if exist src\pl\tcl\win32ver.rc del /q src\pl\tcl\win32ver.rc
+if exist src\test\isolation\win32ver.rc del /q src\test\isolation\win32ver.rc
+if exist src\test\regress\win32ver.rc del /q src\test\regress\win32ver.rc
+if exist src\timezone\win32ver.rc del /q src\timezone\win32ver.rc
+
+for /d %%f in (src\interfaces\ecpg\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc
+for /d %%f in (contrib\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc
+for /d %%f in (src\backend\utils\mb\conversion_procs\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc
+for /d %%f in (src\test\modules\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc
+
+REM Delete files created with GenerateFiles() in Solution.pm
+if exist src\include\pg_config.h del /q src\include\pg_config.h
+if exist src\include\pg_config_ext.h del /q src\include\pg_config_ext.h
+if exist src\include\pg_config_os.h del /q src\include\pg_config_os.h
+if %DIST%==1 if exist src\backend\parser\gram.h del /q src\backend\parser\gram.h
+if exist src\include\utils\errcodes.h del /q src\include\utils\errcodes.h
+if exist src\include\utils\fmgroids.h del /q src\include\utils\fmgroids.h
+if exist src\include\utils\fmgrprotos.h del /q src\include\utils\fmgrprotos.h
+if exist src\include\storage\lwlocknames.h del /q src\include\storage\lwlocknames.h
+if exist src\include\utils\probes.h del /q src\include\utils\probes.h
+if exist src\include\catalog\schemapg.h del /q src\include\catalog\schemapg.h
+if exist src\include\catalog\system_fk_info.h del /q src\include\catalog\system_fk_info.h
+if exist src\include\catalog\pg_*_d.h del /q src\include\catalog\pg_*_d.h
+if exist src\include\catalog\header-stamp del /q src\include\catalog\header-stamp
+if exist doc\src\sgml\version.sgml del /q doc\src\sgml\version.sgml
+
+if %DIST%==1 if exist src\backend\utils\fmgroids.h del /q src\backend\utils\fmgroids.h
+if %DIST%==1 if exist src\backend\utils\fmgrprotos.h del /q src\backend\utils\fmgrprotos.h
+if %DIST%==1 if exist src\backend\utils\fmgrtab.c del /q src\backend\utils\fmgrtab.c
+if %DIST%==1 if exist src\backend\utils\fmgr-stamp del /q src\backend\utils\fmgr-stamp
+if %DIST%==1 if exist src\backend\utils\errcodes.h del /q src\backend\utils\errcodes.h
+if %DIST%==1 if exist src\backend\storage\lmgr\lwlocknames.c del /q src\backend\storage\lmgr\lwlocknames.c
+if %DIST%==1 if exist src\backend\storage\lmgr\lwlocknames.h del /q src\backend\storage\lmgr\lwlocknames.h
+if %DIST%==1 if exist src\pl\plpython\spiexceptions.h del /q src\pl\plpython\spiexceptions.h
+if %DIST%==1 if exist src\pl\plpgsql\src\plerrcodes.h del /q src\pl\plpgsql\src\plerrcodes.h
+if %DIST%==1 if exist src\pl\tcl\pltclerrcodes.h del /q src\pl\tcl\pltclerrcodes.h
+if %DIST%==1 if exist src\bin\psql\sql_help.c del /q src\bin\psql\sql_help.c
+if %DIST%==1 if exist src\bin\psql\sql_help.h del /q src\bin\psql\sql_help.h
+if %DIST%==1 if exist src\common\kwlist_d.h del /q src\common\kwlist_d.h
+if %DIST%==1 if exist src\pl\plpgsql\src\pl_reserved_kwlist_d.h del /q src\pl\plpgsql\src\pl_reserved_kwlist_d.h
+if %DIST%==1 if exist src\pl\plpgsql\src\pl_unreserved_kwlist_d.h del /q src\pl\plpgsql\src\pl_unreserved_kwlist_d.h
+if %DIST%==1 if exist src\interfaces\ecpg\preproc\c_kwlist_d.h del /q src\interfaces\ecpg\preproc\c_kwlist_d.h
+if %DIST%==1 if exist src\interfaces\ecpg\preproc\ecpg_kwlist_d.h del /q src\interfaces\ecpg\preproc\ecpg_kwlist_d.h
+if %DIST%==1 if exist src\interfaces\ecpg\preproc\preproc.y del /q src\interfaces\ecpg\preproc\preproc.y
+if %DIST%==1 if exist src\backend\catalog\postgres.bki del /q src\backend\catalog\postgres.bki
+if %DIST%==1 if exist src\backend\catalog\system_constraints.sql del /q src\backend\catalog\system_constraints.sql
+if %DIST%==1 if exist src\backend\catalog\schemapg.h del /q src\backend\catalog\schemapg.h
+if %DIST%==1 if exist src\backend\catalog\system_fk_info.h del /q src\backend\catalog\system_fk_info.h
+if %DIST%==1 if exist src\backend\catalog\pg_*_d.h del /q src\backend\catalog\pg_*_d.h
+if %DIST%==1 if exist src\backend\catalog\bki-stamp del /q src\backend\catalog\bki-stamp
+if %DIST%==1 if exist src\backend\parser\scan.c del /q src\backend\parser\scan.c
+if %DIST%==1 if exist src\backend\parser\gram.c del /q src\backend\parser\gram.c
+if %DIST%==1 if exist src\backend\bootstrap\bootscanner.c del /q src\backend\bootstrap\bootscanner.c
+if %DIST%==1 if exist src\backend\bootstrap\bootparse.c del /q src\backend\bootstrap\bootparse.c
+if %DIST%==1 if exist src\backend\utils\adt\jsonpath_gram.c del /q src\backend\utils\adt\jsonpath_gram.c
+if %DIST%==1 if exist src\backend\utils\adt\jsonpath_scan.c del /q src\backend\utils\adt\jsonpath_scan.c
+if %DIST%==1 if exist src\backend\utils\misc\guc-file.c del /q src\backend\utils\misc\guc-file.c
+if %DIST%==1 if exist src\backend\replication\repl_scanner.c del /q src\backend\replication\repl_scanner.c
+if %DIST%==1 if exist src\backend\replication\repl_gram.c del /q src\backend\replication\repl_gram.c
+if %DIST%==1 if exist src\backend\replication\syncrep_scanner.c del /q src\backend\replication\syncrep_scanner.c
+if %DIST%==1 if exist src\backend\replication\syncrep_gram.c del /q src\backend\replication\syncrep_gram.c
+
+
+if exist src\interfaces\libpq\libpqdll.def del /q src\interfaces\libpq\libpqdll.def
+if exist src\interfaces\ecpg\compatlib\compatlib.def del /q src\interfaces\ecpg\compatlib\compatlib.def
+if exist src\interfaces\ecpg\ecpglib\ecpglib.def del /q src\interfaces\ecpg\ecpglib\ecpglib.def
+if exist src\interfaces\ecpg\include\ecpg_config.h del /q src\interfaces\ecpg\include\ecpg_config.h
+if exist src\interfaces\ecpg\pgtypeslib\pgtypeslib.def del /q src\interfaces\ecpg\pgtypeslib\pgtypeslib.def
+if %DIST%==1 if exist src\interfaces\ecpg\preproc\pgc.c del /q src\interfaces\ecpg\preproc\pgc.c
+if %DIST%==1 if exist src\interfaces\ecpg\preproc\preproc.c del /q src\interfaces\ecpg\preproc\preproc.c
+if %DIST%==1 if exist src\interfaces\ecpg\preproc\preproc.h del /q src\interfaces\ecpg\preproc\preproc.h
+
+if exist src\port\pg_config_paths.h del /q src\port\pg_config_paths.h
+
+if exist src\pl\plperl\SPI.c del /q src\pl\plperl\SPI.c
+if exist src\pl\plperl\Util.c del /q src\pl\plperl\Util.c
+if exist src\pl\plperl\perlchunks.h del /q src\pl\plperl\perlchunks.h
+if exist src\pl\plperl\plperl_opmask.h del /q src\pl\plperl\plperl_opmask.h
+if %DIST%==1 if exist src\pl\plpgsql\src\pl_gram.c del /q src\pl\plpgsql\src\pl_gram.c
+if %DIST%==1 if exist src\pl\plpgsql\src\pl_gram.h del /q src\pl\plpgsql\src\pl_gram.h
+
+if %DIST%==1 if exist src\fe_utils\psqlscan.c del /q src\fe_utils\psqlscan.c
+if %DIST%==1 if exist src\bin\psql\psqlscanslash.c del /q src\bin\psql\psqlscanslash.c
+if %DIST%==1 if exist src\bin\pgbench\exprscan.c del /q src\bin\pgbench\exprscan.c
+if %DIST%==1 if exist src\bin\pgbench\exprparse.c del /q src\bin\pgbench\exprparse.c
+
+if %DIST%==1 if exist contrib\cube\cubescan.c del /q contrib\cube\cubescan.c
+if %DIST%==1 if exist contrib\cube\cubeparse.c del /q contrib\cube\cubeparse.c
+if %DIST%==1 if exist contrib\seg\segscan.c del /q contrib\seg\segscan.c
+if %DIST%==1 if exist contrib\seg\segparse.c del /q contrib\seg\segparse.c
+
+if exist src\test\regress\tmp_check rd /s /q src\test\regress\tmp_check
+if exist contrib\spi\refint.dll del /q contrib\spi\refint.dll
+if exist contrib\spi\autoinc.dll del /q contrib\spi\autoinc.dll
+if exist src\test\regress\regress.dll del /q src\test\regress\regress.dll
+if exist src\test\regress\refint.dll del /q src\test\regress\refint.dll
+if exist src\test\regress\autoinc.dll del /q src\test\regress\autoinc.dll
+if %DIST%==1 if exist src\test\isolation\specscanner.c del /q src\test\isolation\specscanner.c
+if %DIST%==1 if exist src\test\isolation\specparse.c del /q src\test\isolation\specparse.c
+
+for /d %%f in (contrib\* src\bin\* src\test\* src\test\modules\*
+ ) do if exist %%f\tmp_check rd /s /q %%f\tmp_check
+
+REM Clean up datafiles built with contrib
+REM cd contrib
+REM for /r %%f in (*.sql) do if exist %%f.in del %%f
+
+cd "%~dp0"
+
+REM Clean up ecpg regression test files
+msbuild ecpg_regression.proj /NoLogo /v:q %MSBFLAGS% /t:clean
+
+goto :eof
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
new file mode 100644
index 0000000..186849a
--- /dev/null
+++ b/src/tools/msvc/config_default.pl
@@ -0,0 +1,32 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+# Configuration arguments for vcbuild.
+use strict;
+use warnings;
+
+our $config = {
+ asserts => 0, # --enable-cassert
+
+ # blocksize => 8, # --with-blocksize, 8kB by default
+ # wal_blocksize => 8, # --with-wal-blocksize, 8kB by default
+ ldap => 1, # --with-ldap
+ extraver => undef, # --with-extra-version=<string>
+ gss => undef, # --with-gssapi=<path>
+ icu => undef, # --with-icu=<path>
+ lz4 => undef, # --with-lz4=<path>
+ zstd => undef, # --with-zstd=<path>
+ nls => undef, # --enable-nls=<path>
+ tap_tests => undef, # --enable-tap-tests
+ tcl => undef, # --with-tcl=<path>
+ perl => undef, # --with-perl=<path>
+ python => undef, # --with-python=<path>
+ openssl => undef, # --with-ssl=openssl with <path>
+ uuid => undef, # --with-uuid=<path>
+ xml => undef, # --with-libxml=<path>
+ xslt => undef, # --with-libxslt=<path>
+ iconv => undef, # (not in configure, path to iconv)
+ zlib => undef # --with-zlib=<path>
+};
+
+1;
diff --git a/src/tools/msvc/dummylib/README b/src/tools/msvc/dummylib/README
new file mode 100644
index 0000000..7b63d0e
--- /dev/null
+++ b/src/tools/msvc/dummylib/README
@@ -0,0 +1,13 @@
+
+src/tools/msvc/dummylib
+
+This directory contains just enough of a dummy library to allow checking of
+the programs in src/tools/msvc and src/tools/win32tzlist.pl with
+perl -cw, even on machines that lack the Win32 perl infrastructure.
+
+invoke via:
+
+PERL5LIB=src/tools/msvc/dummylib perl -cw $file
+
+This is the only use that should be made of this directory. Attempting actually
+running of any programs using this library will result in a lot of grief.
diff --git a/src/tools/msvc/dummylib/Win32.pm b/src/tools/msvc/dummylib/Win32.pm
new file mode 100644
index 0000000..1c7e3d3
--- /dev/null
+++ b/src/tools/msvc/dummylib/Win32.pm
@@ -0,0 +1,7 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+package Win32;
+use strict;
+use warnings;
+1;
diff --git a/src/tools/msvc/dummylib/Win32/Registry.pm b/src/tools/msvc/dummylib/Win32/Registry.pm
new file mode 100644
index 0000000..d995432
--- /dev/null
+++ b/src/tools/msvc/dummylib/Win32/Registry.pm
@@ -0,0 +1,16 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+package Win32::Registry;
+
+use strict;
+use warnings;
+
+use vars qw($HKEY_LOCAL_MACHINE);
+
+use Exporter ();
+our (@EXPORT, @ISA);
+@ISA = qw(Exporter);
+@EXPORT = qw($HKEY_LOCAL_MACHINE);
+
+1;
diff --git a/src/tools/msvc/dummylib/Win32API/File.pm b/src/tools/msvc/dummylib/Win32API/File.pm
new file mode 100644
index 0000000..d77e48b
--- /dev/null
+++ b/src/tools/msvc/dummylib/Win32API/File.pm
@@ -0,0 +1,17 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+package Win32API::File;
+
+use strict;
+use warnings;
+
+use constant { SEM_FAILCRITICALERRORS => 1, SEM_NOGPFAULTERRORBOX => 2 };
+sub SetErrormode { }
+use Exporter;
+our (@ISA, @EXPORT_OK, %EXPORT_TAGS);
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(SetErrorMode SEM_FAILCRITICALERRORS SEM_NOGPFAULTERRORBOX);
+%EXPORT_TAGS = (SEM_ => [qw(SEM_FAILCRITICALERRORS SEM_NOGPFAULTERRORBOX)]);
+
+1;
diff --git a/src/tools/msvc/ecpg_regression.proj b/src/tools/msvc/ecpg_regression.proj
new file mode 100644
index 0000000..ec2760b
--- /dev/null
+++ b/src/tools/msvc/ecpg_regression.proj
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="all">
+<!--
+ MSBuild project file to build ecpg regression tests
+-->
+
+ <PropertyGroup>
+ <TESTDIR>..\..\interfaces\ecpg\test</TESTDIR>
+ <CONFIG>Debug</CONFIG>
+ <OUTDIR>..\..\..\..\..\$(CONFIG)\</OUTDIR>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(CONFIG)'=='DEBUG'">
+ <!-- set debug runtime library if necessary to be compatible with the LIB files generated -->
+ <DEBUGLIB>d</DEBUGLIB>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Pgc Include="$(TESTDIR)\**\*.pgc" Exclude="$(TESTDIR)\performance\perftest.pgc"/>
+ </ItemGroup>
+
+ <ItemGroup>
+ <OutputToDelete Include="$(TESTDIR)\**\*.exe" />
+ <OutputToDelete Include="$(TESTDIR)\**\*.exe.manifest" />
+ <OutputToDelete Include="$(TESTDIR)\**\*.obj" />
+ <OutputToDelete Include="$(TESTDIR)\**\*.c" Exclude="$(TESTDIR)\pg_regress_ecpg.c;$(TESTDIR)\expected\*.c" />
+ </ItemGroup>
+
+ <Target Name="all" Inputs="@(Pgc)" Outputs="%(RelativeDir)%(Filename).exe">
+ <!-- Set special parameters for some tests -->
+ <CreateProperty Value="-C INFORMIX" Condition="'%(Pgc.RelativeDir)'=='$(TESTDIR)\compat_informix\'">
+ <Output TaskParameter="Value" PropertyName="ECPGPARAM" />
+ </CreateProperty>
+ <CreateProperty Value="-C INFORMIX -r no_indicator" Condition="'%(Pgc.FileName)'=='rnull'">
+ <Output TaskParameter="Value" PropertyName="ECPGPARAM" />
+ </CreateProperty>
+ <CreateProperty Value="-C ORACLE" Condition="'%(Pgc.RelativeDir)'=='$(TESTDIR)\compat_oracle\'">
+ <Output TaskParameter="Value" PropertyName="ECPGPARAM" />
+ </CreateProperty>
+ <CreateProperty Value="-c" Condition="'%(Pgc.FileName)'=='array_of_struct'">
+ <Output TaskParameter="Value" PropertyName="ECPGPARAM" />
+ </CreateProperty>
+ <CreateProperty Value="-c" Condition="'%(Pgc.FileName)'=='pointer_to_struct'">
+ <Output TaskParameter="Value" PropertyName="ECPGPARAM" />
+ </CreateProperty>
+ <CreateProperty Value="-r questionmarks" Condition="'%(Pgc.FileName)'=='oldexec'">
+ <Output TaskParameter="Value" PropertyName="ECPGPARAM" />
+ </CreateProperty>
+ <CreateProperty Value="-r prepare" Condition="'%(Pgc.FileName)'=='autoprep'">
+ <Output TaskParameter="Value" PropertyName="ECPGPARAM" />
+ </CreateProperty>
+ <CreateProperty Value="-i" Condition="'%(Pgc.FileName)'=='strings'">
+ <Output TaskParameter="Value" PropertyName="ECPGPARAM" />
+ </CreateProperty>
+
+ <!-- Run ECPG and the Visual C++ compiler on the files. Don't bother with dependency check between the steps -->
+ <Exec WorkingDirectory="%(Pgc.RelativeDir)" Command="$(OUTDIR)ecpg\ecpg -I ../../include --regression $(ECPGPARAM) -o %(Pgc.Filename).c %(Pgc.Filename).pgc" />
+ <Exec WorkingDirectory="%(Pgc.RelativeDir)" Command="cl /nologo %(Pgc.FileName).c /TC /MD$(DEBUGLIB) /DENABLE_THREAD_SAFETY /DWIN32 /I. /I..\..\include /I..\..\..\libpq /I..\..\..\..\include /link /defaultlib:$(OUTDIR)libecpg\libecpg.lib /defaultlib:$(OUTDIR)libecpg_compat\libecpg_compat.lib /defaultlib:$(OUTDIR)libpgtypes\libpgtypes.lib" />
+ </Target>
+
+ <!-- Clean up all output files -->
+ <Target Name="clean">
+ <Delete Files="@(OutputToDelete)" />
+ </Target>
+</Project>
diff --git a/src/tools/msvc/gendef.pl b/src/tools/msvc/gendef.pl
new file mode 100644
index 0000000..b8c514a
--- /dev/null
+++ b/src/tools/msvc/gendef.pl
@@ -0,0 +1,181 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+use 5.8.0;
+use List::Util qw(max);
+
+my @def;
+
+#
+# Script that generates a .DEF file for all objects in a directory
+#
+# src/tools/msvc/gendef.pl
+#
+
+# Given a symbol file path, loops over its contents
+# and returns a list of symbols of interest as a dictionary
+# of 'symbolname' -> symtype, where symtype is:
+#
+# 0 a CODE symbol, left undecorated in the .DEF
+# 1 A DATA symbol, i.e. global var export
+#
+sub extract_syms
+{
+ my ($symfile, $def) = @_;
+ open(my $f, '<', $symfile) || die "Could not open $symfile for $_: $!\n";
+ while (<$f>)
+ {
+
+ # Expected symbol lines look like:
+ #
+ # 0 1 2 3 4 5 6
+ # IDX SYMBOL SECT SYMTYPE SYMSTATIC SYMNAME
+ # ------------------------------------------------------------------------
+ # 02E 00000130 SECTA notype External | _standbyState
+ # 02F 00000009 SECT9 notype Static | _LocalRecoveryInProgress
+ # 064 00000020 SECTC notype () Static | _XLogCheckBuffer
+ # 065 00000000 UNDEF notype () External | _BufferGetTag
+ #
+ # See http://msdn.microsoft.com/en-us/library/b842y285.aspx
+ #
+ # We're not interested in the symbol index or offset.
+ #
+ # SECT[ION] is only examined to see whether the symbol is defined in a
+ # COFF section of the local object file; if UNDEF, it's a symbol to be
+ # resolved at link time from another object so we can't export it.
+ #
+ # SYMTYPE is always notype for C symbols as there's no typeinfo and no
+ # way to get the symbol type from name (de)mangling. However, we care
+ # if "notype" is suffixed by "()" or not. The presence of () means the
+ # symbol is a function, the absence means it isn't.
+ #
+ # SYMSTATIC indicates whether it's a compilation-unit local "static"
+ # symbol ("Static"), or whether it's available for use from other
+ # compilation units ("External"). We export all symbols that aren't
+ # static as part of the whole program DLL interface to produce UNIX-like
+ # default linkage.
+ #
+ # SYMNAME is, obviously, the symbol name. The leading underscore
+ # indicates that the _cdecl calling convention is used. See
+ # http://www.unixwiz.net/techtips/win32-callconv.html
+ # http://www.codeproject.com/Articles/1388/Calling-Conventions-Demystified
+ #
+ s/notype \(\)/func/g;
+ s/notype/data/g;
+
+ my @pieces = split;
+
+ # Skip file and section headers and other non-symbol entries
+ next unless defined($pieces[0]) and $pieces[0] =~ /^[A-F0-9]{3,}$/;
+
+ # Skip blank symbol names
+ next unless $pieces[6];
+
+ # Skip externs used from another compilation unit
+ next if ($pieces[2] eq "UNDEF");
+
+ # Skip static symbols
+ next unless ($pieces[4] eq "External");
+
+ # Skip some more MSVC-generated crud
+ next if $pieces[6] =~ /^@/;
+ next if $pieces[6] =~ /^\(/;
+
+ # __real and __xmm are out-of-line floating point literals and
+ # (for __xmm) their SIMD equivalents. They shouldn't be part
+ # of the DLL interface.
+ next if $pieces[6] =~ /^__real/;
+ next if $pieces[6] =~ /^__xmm/;
+
+ # __imp entries are imports from other DLLs, eg __imp__malloc .
+ # (We should never have one of these that hasn't already been skipped
+ # by the UNDEF test above, though).
+ next if $pieces[6] =~ /^__imp/;
+
+ # More under-documented internal crud
+ next if $pieces[6] =~ /NULL_THUNK_DATA$/;
+ next if $pieces[6] =~ /^__IMPORT_DESCRIPTOR/;
+ next if $pieces[6] =~ /^__NULL_IMPORT/;
+
+ # Skip string literals
+ next if $pieces[6] =~ /^\?\?_C/;
+
+ # We assume that if a symbol is defined as data, then as a function,
+ # the linker will reject the binary anyway. So it's OK to just pick
+ # whatever came last.
+ $def->{ $pieces[6] } = $pieces[3];
+ }
+ close($f);
+ return;
+}
+
+sub writedef
+{
+ my ($deffile, $platform, $def) = @_;
+ open(my $fh, '>', $deffile) || die "Could not write to $deffile\n";
+ print $fh "EXPORTS\n";
+ foreach my $f (sort keys %{$def})
+ {
+ my $isdata = $def->{$f} eq 'data';
+
+ # Strip the leading underscore for win32, but not x64
+ $f =~ s/^_//
+ unless ($platform eq "x64");
+
+ # Emit just the name if it's a function symbol, or emit the name
+ # decorated with the DATA option for variables.
+ if ($isdata)
+ {
+ print $fh " $f DATA\n";
+ }
+ else
+ {
+ print $fh " $f\n";
+ }
+ }
+ close($fh);
+ return;
+}
+
+
+sub usage
+{
+ die( "Usage: gendef.pl <modulepath> <platform>\n"
+ . " modulepath: path to dir with obj files, no trailing slash"
+ . " platform: Win32 | x64");
+}
+
+usage()
+ unless scalar(@ARGV) == 2
+ && ( ($ARGV[0] =~ /\\([^\\]+$)/)
+ && ($ARGV[1] eq 'Win32' || $ARGV[1] eq 'x64'));
+my $defname = uc $1;
+my $deffile = "$ARGV[0]/$defname.def";
+my $platform = $ARGV[1];
+
+# if the def file exists and is newer than all input object files, skip
+# its creation
+if (-f $deffile
+ && (-M $deffile > max(map { -M } <$ARGV[0]/*.obj>)))
+{
+ print "Not re-generating $defname.DEF, file already exists.\n";
+ exit(0);
+}
+
+print "Generating $defname.DEF from directory $ARGV[0], platform $platform\n";
+
+my %def = ();
+
+my $symfile = "$ARGV[0]/all.sym";
+my $tmpfile = "$ARGV[0]/tmp.sym";
+system("dumpbin /symbols /out:$tmpfile $ARGV[0]/*.obj >NUL")
+ && die "Could not call dumpbin";
+rename($tmpfile, $symfile);
+extract_syms($symfile, \%def);
+print "\n";
+
+writedef($deffile, $platform, \%def);
+
+print "Generated " . scalar(keys(%def)) . " symbols\n";
diff --git a/src/tools/msvc/install.bat b/src/tools/msvc/install.bat
new file mode 100644
index 0000000..d02b808
--- /dev/null
+++ b/src/tools/msvc/install.bat
@@ -0,0 +1,6 @@
+@echo off
+REM src/tools/msvc/install.bat
+REM all the logic for this now belongs in install.pl. This file really
+REM only exists so you don't have to type "perl install.pl"
+REM Resist any temptation to add any logic here.
+@perl %~dp0/install.pl %*
diff --git a/src/tools/msvc/install.pl b/src/tools/msvc/install.pl
new file mode 100755
index 0000000..142d0dd
--- /dev/null
+++ b/src/tools/msvc/install.pl
@@ -0,0 +1,39 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+#
+# Script that provides 'make install' functionality for msvc builds
+#
+# src/tools/msvc/install.pl
+#
+use strict;
+use warnings;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use Install qw(Install);
+
+# buildenv.pl is for specifying the build environment settings
+# it should contain lines like:
+# $ENV{PATH} = "c:/path/to/bison/bin;$ENV{PATH}";
+
+if (-e "src/tools/msvc/buildenv.pl")
+{
+ do "./src/tools/msvc/buildenv.pl";
+}
+elsif (-e "./buildenv.pl")
+{
+ do "./buildenv.pl";
+}
+
+my $target = shift || Usage();
+my $insttype = shift;
+Install($target, $insttype);
+
+sub Usage
+{
+ print "Usage: install.pl <targetdir> [installtype]\n";
+ print "installtype: client\n";
+ exit(1);
+}
diff --git a/src/tools/msvc/mkvcbuild.pl b/src/tools/msvc/mkvcbuild.pl
new file mode 100644
index 0000000..7c9f7ce
--- /dev/null
+++ b/src/tools/msvc/mkvcbuild.pl
@@ -0,0 +1,31 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+#
+# Script that parses Unix style build environment and generates build files
+# for building with Visual Studio.
+#
+# src/tools/msvc/mkvcbuild.pl
+#
+use strict;
+use warnings;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use Mkvcbuild;
+
+chdir('../../..') if (-d '../msvc' && -d '../../../src');
+die 'Must run from root or msvc directory'
+ unless (-d 'src/tools/msvc' && -d 'src');
+
+die 'Could not find config_default.pl'
+ unless (-f 'src/tools/msvc/config_default.pl');
+print "Warning: no config.pl found, using default.\n"
+ unless (-f 'src/tools/msvc/config.pl');
+
+our $config;
+do './src/tools/msvc/config_default.pl';
+do './src/tools/msvc/config.pl' if (-f 'src/tools/msvc/config.pl');
+
+Mkvcbuild::mkvcbuild($config);
diff --git a/src/tools/msvc/pgbison.bat b/src/tools/msvc/pgbison.bat
new file mode 100755
index 0000000..dc8ac4a
--- /dev/null
+++ b/src/tools/msvc/pgbison.bat
@@ -0,0 +1,7 @@
+@echo off
+
+REM src/tools/msvc/pgbison.bat
+REM all the logic for this now belongs in pgbison.pl. This file really
+REM only exists so you don't have to type "perl src/tools/msvc/pgbison.pl"
+REM Resist any temptation to add any logic here.
+@perl %~dp0/pgbison.pl %*
diff --git a/src/tools/msvc/pgbison.pl b/src/tools/msvc/pgbison.pl
new file mode 100644
index 0000000..3974c30
--- /dev/null
+++ b/src/tools/msvc/pgbison.pl
@@ -0,0 +1,55 @@
+# -*-perl-*- hey - emacs - this is a perl file
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+# src/tools/msvc/pgbison.pl
+
+use strict;
+use warnings;
+
+use File::Basename;
+
+# assume we are in the postgres source root
+
+do './src/tools/msvc/buildenv.pl' if -e 'src/tools/msvc/buildenv.pl';
+
+my ($bisonver) = `bison -V`; # grab first line
+$bisonver = (split(/\s+/, $bisonver))[3]; # grab version number
+
+unless ($bisonver eq '1.875' || $bisonver ge '2.2')
+{
+ print "WARNING! Bison install not found, or unsupported Bison version.\n";
+ print "echo Attempting to build without.\n";
+ exit 0;
+}
+
+my $input = shift;
+if ($input !~ /\.y$/)
+{
+ print "Input must be a .y file\n";
+ exit 1;
+}
+elsif (!-e $input)
+{
+ print "Input file $input not found\n";
+ exit 1;
+}
+
+(my $output = $input) =~ s/\.y$/.c/;
+
+# plpgsql just has to be different
+$output =~ s/gram\.c$/pl_gram.c/ if $input =~ /src.pl.plpgsql.src.gram\.y$/;
+
+my $makefile = dirname($input) . "/Makefile";
+my ($mf, $make);
+open($mf, '<', $makefile);
+local $/ = undef;
+$make = <$mf>;
+close($mf);
+my $basetarg = basename($output);
+my $headerflag = ($make =~ /^$basetarg:\s+BISONFLAGS\b.*-d/m ? '-d' : '');
+
+my $nodep = $bisonver ge '3.0' ? "-Wno-deprecated" : "";
+
+system("bison $nodep $headerflag $input -o $output");
+exit $? >> 8;
diff --git a/src/tools/msvc/pgflex.bat b/src/tools/msvc/pgflex.bat
new file mode 100755
index 0000000..f20834b
--- /dev/null
+++ b/src/tools/msvc/pgflex.bat
@@ -0,0 +1,7 @@
+@echo off
+
+REM src/tools/msvc/pgflex.bat
+REM all the logic for this now belongs in pgflex.pl. This file really
+REM only exists so you don't have to type "perl src/tools/msvc/pgflex.pl"
+REM Resist any temptation to add any logic here.
+@perl %~dp0/pgflex.pl %*
diff --git a/src/tools/msvc/pgflex.pl b/src/tools/msvc/pgflex.pl
new file mode 100644
index 0000000..eb31983
--- /dev/null
+++ b/src/tools/msvc/pgflex.pl
@@ -0,0 +1,108 @@
+# -*-perl-*- hey - emacs - this is a perl file
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+# src/tools/msvc/pgflex.pl
+
+use strict;
+use warnings;
+
+use File::Basename;
+
+# silence flex bleatings about file path style
+$ENV{CYGWIN} = 'nodosfilewarning';
+
+# assume we are in the postgres source root
+
+do './src/tools/msvc/buildenv.pl' if -e 'src/tools/msvc/buildenv.pl';
+
+my ($flexver) = `flex -V`; # grab first line
+$flexver = (split(/\s+/, $flexver))[1];
+$flexver =~ s/[^0-9.]//g;
+my @verparts = split(/\./, $flexver);
+unless ($verparts[0] == 2
+ && ($verparts[1] > 5 || ($verparts[1] == 5 && $verparts[2] >= 31)))
+{
+ print "WARNING! Flex install not found, or unsupported Flex version.\n";
+ print "echo Attempting to build without.\n";
+ exit 0;
+}
+
+my $input = shift;
+if ($input !~ /\.l$/)
+{
+ print "Input must be a .l file\n";
+ exit 1;
+}
+elsif (!-e $input)
+{
+ print "Input file $input not found\n";
+ exit 1;
+}
+
+(my $output = $input) =~ s/\.l$/.c/;
+
+# get flex flags from make file
+my $makefile = dirname($input) . "/Makefile";
+my ($mf, $make);
+open($mf, '<', $makefile);
+local $/ = undef;
+$make = <$mf>;
+close($mf);
+my $basetarg = basename($output);
+my $flexflags = ($make =~ /^$basetarg:\s*FLEXFLAGS\s*=\s*(\S.*)/m ? $1 : '');
+
+system("flex $flexflags -o$output $input");
+if ($? == 0)
+{
+
+ # Check for "%option reentrant" in .l file.
+ my $lfile;
+ open($lfile, '<', $input) || die "opening $input for reading: $!";
+ my $lcode = <$lfile>;
+ close($lfile);
+ if ($lcode =~ /\%option\sreentrant/)
+ {
+
+ # Reentrant scanners usually need a fix to prevent
+ # "unused variable" warnings with older flex versions.
+ system("perl src\\tools\\fix-old-flex-code.pl $output");
+ }
+ else
+ {
+
+ # For non-reentrant scanners we need to fix up the yywrap
+ # macro definition to keep the MS compiler happy.
+ # For reentrant scanners (like the core scanner) we do not
+ # need to (and must not) change the yywrap definition.
+ my $cfile;
+ open($cfile, '<', $output) || die "opening $output for reading: $!";
+ my $ccode = <$cfile>;
+ close($cfile);
+ $ccode =~ s/yywrap\(n\)/yywrap()/;
+ open($cfile, '>', $output) || die "opening $output for writing: $!";
+ print $cfile $ccode;
+ close($cfile);
+ }
+ if ($flexflags =~ /\s-b\s/)
+ {
+ my $lexback = "lex.backup";
+ open($lfile, '<', $lexback) || die "opening $lexback for reading: $!";
+ my $lexbacklines = <$lfile>;
+ close($lfile);
+ my $linecount = $lexbacklines =~ tr /\n/\n/;
+ if ($linecount != 1)
+ {
+ print "Scanner requires backup, see lex.backup.\n";
+ exit 1;
+ }
+ unlink $lexback;
+ }
+
+ exit 0;
+
+}
+else
+{
+ exit $? >> 8;
+}
diff --git a/src/tools/msvc/vcregress.bat b/src/tools/msvc/vcregress.bat
new file mode 100644
index 0000000..7fba45c
--- /dev/null
+++ b/src/tools/msvc/vcregress.bat
@@ -0,0 +1,6 @@
+@echo off
+REM src/tools/msvc/vcregress.bat
+REM all the logic for this now belongs in vcregress.pl. This file really
+REM only exists so you don't have to type "perl vcregress.pl"
+REM Resist any temptation to add any logic here.
+@perl %~dp0/vcregress.pl %*
diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl
new file mode 100644
index 0000000..74fb735
--- /dev/null
+++ b/src/tools/msvc/vcregress.pl
@@ -0,0 +1,660 @@
+# -*-perl-*- hey - emacs - this is a perl file
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+# src/tools/msvc/vcregress.pl
+
+use strict;
+use warnings;
+
+our $config;
+
+use Cwd;
+use File::Basename;
+use File::Copy;
+use File::Find ();
+use File::Path qw(rmtree);
+use File::Spec qw(devnull);
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use Install qw(Install);
+
+my $startdir = getcwd();
+
+chdir "../../.." if (-d "../../../src/tools/msvc");
+
+my $topdir = getcwd();
+my $tmp_installdir = "$topdir/tmp_install";
+
+do './src/tools/msvc/config_default.pl';
+do './src/tools/msvc/config.pl' if (-f 'src/tools/msvc/config.pl');
+
+my $devnull = File::Spec->devnull;
+
+# These values are defaults that can be overridden by the calling environment
+# (see buildenv.pl processing below). We assume that the ones listed here
+# always exist by default. Other values may optionally be set for bincheck
+# or taptest, see set_command_env() below.
+# c.f. src/Makefile.global.in and configure.ac
+$ENV{TAR} ||= 'tar';
+
+# buildenv.pl is for specifying the build environment settings
+# it should contain lines like:
+# $ENV{PATH} = "c:/path/to/bison/bin;$ENV{PATH}";
+
+if (-e "src/tools/msvc/buildenv.pl")
+{
+ do "./src/tools/msvc/buildenv.pl";
+}
+
+my $what = shift || "";
+if ($what =~
+ /^(check|installcheck|plcheck|contribcheck|modulescheck|ecpgcheck|isolationcheck|upgradecheck|bincheck|recoverycheck|taptest)$/i
+ )
+{
+ $what = uc $what;
+}
+else
+{
+ usage();
+}
+
+# use a capital C here because config.pl has $config
+my $Config = -e "release/postgres/postgres.exe" ? "Release" : "Debug";
+
+copy("$Config/refint/refint.dll", "src/test/regress");
+copy("$Config/autoinc/autoinc.dll", "src/test/regress");
+copy("$Config/regress/regress.dll", "src/test/regress");
+copy("$Config/dummy_seclabel/dummy_seclabel.dll", "src/test/regress");
+
+# Configuration settings used by TAP tests
+$ENV{with_ssl} = $config->{openssl} ? 'openssl' : 'no';
+$ENV{with_ldap} = $config->{ldap} ? 'yes' : 'no';
+$ENV{with_icu} = $config->{icu} ? 'yes' : 'no';
+$ENV{with_gssapi} = $config->{gss} ? 'yes' : 'no';
+$ENV{with_krb_srvnam} = $config->{krb_srvnam} || 'postgres';
+$ENV{with_readline} = 'no';
+
+$ENV{PATH} = "$topdir/$Config/libpq;$ENV{PATH}";
+
+if ($ENV{PERL5LIB})
+{
+ $ENV{PERL5LIB} = "$topdir/src/tools/msvc;$ENV{PERL5LIB}";
+}
+else
+{
+ $ENV{PERL5LIB} = "$topdir/src/tools/msvc";
+}
+
+my $maxconn = "";
+$maxconn = "--max-connections=$ENV{MAX_CONNECTIONS}"
+ if $ENV{MAX_CONNECTIONS};
+
+my $temp_config = "";
+$temp_config = "--temp-config=\"$ENV{TEMP_CONFIG}\""
+ if $ENV{TEMP_CONFIG};
+
+chdir "src/test/regress";
+
+my %command = (
+ CHECK => \&check,
+ PLCHECK => \&plcheck,
+ INSTALLCHECK => \&installcheck,
+ ECPGCHECK => \&ecpgcheck,
+ CONTRIBCHECK => \&contribcheck,
+ MODULESCHECK => \&modulescheck,
+ ISOLATIONCHECK => \&isolationcheck,
+ BINCHECK => \&bincheck,
+ RECOVERYCHECK => \&recoverycheck,
+ UPGRADECHECK => \&upgradecheck, # no-op
+ TAPTEST => \&taptest,);
+
+my $proc = $command{$what};
+
+exit 3 unless $proc;
+
+&$proc(@ARGV);
+
+exit 0;
+
+########################################################################
+
+# Helper function for set_command_env, to set one environment command.
+sub set_single_env
+{
+ my $envname = shift;
+ my $envdefault = shift;
+
+ # If a command is defined by the environment, just use it.
+ return if (defined($ENV{$envname}));
+
+ # Nothing is defined, so attempt to assign a default. The command
+ # may not be in the current environment, hence check if it can be
+ # executed.
+ my $rc = system("$envdefault --version >$devnull 2>&1");
+
+ # Set the environment to the default if it exists, else leave it.
+ $ENV{$envname} = $envdefault if $rc == 0;
+ return;
+}
+
+# Set environment values for various command types. These can be used
+# in the TAP tests.
+sub set_command_env
+{
+ set_single_env('GZIP_PROGRAM', 'gzip');
+ set_single_env('LZ4', 'lz4');
+ set_single_env('ZSTD', 'zstd');
+}
+
+sub installcheck_internal
+{
+ my ($schedule, @EXTRA_REGRESS_OPTS) = @_;
+ # for backwards compatibility, "serial" runs the tests in
+ # parallel_schedule one by one.
+ my $maxconn = $maxconn;
+ $maxconn = "--max-connections=1" if $schedule eq 'serial';
+ $schedule = 'parallel' if $schedule eq 'serial';
+
+ my @args = (
+ "../../../$Config/pg_regress/pg_regress",
+ "--dlpath=.",
+ "--bindir=../../../$Config/psql",
+ "--schedule=${schedule}_schedule",
+ "--max-concurrent-tests=20");
+ push(@args, $maxconn) if $maxconn;
+ push(@args, @EXTRA_REGRESS_OPTS);
+ system(@args);
+ my $status = $? >> 8;
+ exit $status if $status;
+ return;
+}
+
+sub installcheck
+{
+ my $schedule = shift || 'serial';
+ installcheck_internal($schedule);
+ return;
+}
+
+sub check
+{
+ my $schedule = shift || 'parallel';
+ # for backwards compatibility, "serial" runs the tests in
+ # parallel_schedule one by one.
+ my $maxconn = $maxconn;
+ $maxconn = "--max-connections=1" if $schedule eq 'serial';
+ $schedule = 'parallel' if $schedule eq 'serial';
+
+ InstallTemp();
+ chdir "${topdir}/src/test/regress";
+ my @args = (
+ "../../../$Config/pg_regress/pg_regress",
+ "--dlpath=.",
+ "--bindir=",
+ "--schedule=${schedule}_schedule",
+ "--max-concurrent-tests=20",
+ "--encoding=SQL_ASCII",
+ "--no-locale",
+ "--temp-instance=./tmp_check");
+ push(@args, $maxconn) if $maxconn;
+ push(@args, $temp_config) if $temp_config;
+ system(@args);
+ my $status = $? >> 8;
+ exit $status if $status;
+ return;
+}
+
+sub ecpgcheck
+{
+ my $msbflags = $ENV{MSBFLAGS} || "";
+ chdir $startdir;
+ system("msbuild ecpg_regression.proj $msbflags /p:config=$Config");
+ my $status = $? >> 8;
+ exit $status if $status;
+ InstallTemp();
+ chdir "$topdir/src/interfaces/ecpg/test";
+ my $schedule = "ecpg";
+ my @args = (
+ "../../../../$Config/pg_regress_ecpg/pg_regress_ecpg",
+ "--bindir=",
+ "--dbname=ecpg1_regression,ecpg2_regression",
+ "--create-role=regress_ecpg_user1,regress_ecpg_user2",
+ "--schedule=${schedule}_schedule",
+ "--encoding=SQL_ASCII",
+ "--no-locale",
+ "--temp-instance=./tmp_chk");
+ push(@args, $maxconn) if $maxconn;
+ system(@args);
+ $status = $? >> 8;
+ exit $status if $status;
+ return;
+}
+
+sub isolationcheck
+{
+ chdir "../isolation";
+ copy("../../../$Config/isolationtester/isolationtester.exe",
+ "../../../$Config/pg_isolation_regress");
+ my @args = (
+ "../../../$Config/pg_isolation_regress/pg_isolation_regress",
+ "--bindir=../../../$Config/psql",
+ "--inputdir=.",
+ "--schedule=./isolation_schedule");
+ push(@args, $maxconn) if $maxconn;
+ system(@args);
+ my $status = $? >> 8;
+ exit $status if $status;
+ return;
+}
+
+sub tap_check
+{
+ die "Tap tests not enabled in configuration"
+ unless $config->{tap_tests};
+
+ my @flags;
+ foreach my $arg (0 .. scalar(@_) - 1)
+ {
+ next unless $_[$arg] =~ /^PROVE_FLAGS=(.*)/;
+ @flags = split(/\s+/, $1);
+ splice(@_, $arg, 1);
+ last;
+ }
+
+ my $dir = shift;
+ chdir $dir;
+
+ # Fetch and adjust PROVE_TESTS, applying glob() to each element
+ # defined to build a list of all the tests matching patterns.
+ my $prove_tests_val = $ENV{PROVE_TESTS} || "t/*.pl";
+ my @prove_tests_array = split(/\s+/, $prove_tests_val);
+ my @prove_tests = ();
+ foreach (@prove_tests_array)
+ {
+ push(@prove_tests, glob($_));
+ }
+
+ # Fetch and adjust PROVE_FLAGS, handling multiple arguments.
+ my $prove_flags_val = $ENV{PROVE_FLAGS} || "";
+ my @prove_flags = split(/\s+/, $prove_flags_val);
+
+ my @args = ("prove", @flags, @prove_tests, @prove_flags);
+
+ # adjust the environment for just this test
+ local %ENV = %ENV;
+ $ENV{PERL5LIB} = "$topdir/src/test/perl;$ENV{PERL5LIB}";
+ $ENV{PG_REGRESS} = "$topdir/$Config/pg_regress/pg_regress";
+ $ENV{REGRESS_SHLIB} = "$topdir/src/test/regress/regress.dll";
+
+ $ENV{TESTDIR} = "$dir";
+ my $module = basename $dir;
+ # add the module build dir as the second element in the PATH
+ $ENV{PATH} =~ s!;!;$topdir/$Config/$module;!;
+
+ rmtree('tmp_check');
+ system(@args);
+ my $status = $? >> 8;
+ return $status;
+}
+
+sub bincheck
+{
+ InstallTemp();
+
+ set_command_env();
+
+ my $mstat = 0;
+
+ # Find out all the existing TAP tests by looking for t/ directories
+ # in the tree.
+ my @bin_dirs = glob("$topdir/src/bin/*");
+
+ # Process each test
+ foreach my $dir (@bin_dirs)
+ {
+ next unless -d "$dir/t";
+
+ my $status = tap_check($dir);
+ $mstat ||= $status;
+ }
+ exit $mstat if $mstat;
+ return;
+}
+
+sub taptest
+{
+ my $dir = shift;
+ my @args;
+
+ if ($dir =~ /^PROVE_FLAGS=/)
+ {
+ push(@args, $dir);
+ $dir = shift;
+ }
+
+ die "no tests found!" unless -d "$topdir/$dir/t";
+
+ push(@args, "$topdir/$dir");
+
+ InstallTemp();
+
+ set_command_env();
+
+ my $status = tap_check(@args);
+ exit $status if $status;
+ return;
+}
+
+sub plcheck
+{
+ chdir "$topdir/src/pl";
+
+ foreach my $dir (glob("*/src *"))
+ {
+ next unless -d "$dir/sql" && -d "$dir/expected";
+ my $lang;
+ if ($dir eq 'plpgsql/src')
+ {
+ $lang = 'plpgsql';
+ }
+ elsif ($dir eq 'tcl')
+ {
+ $lang = 'pltcl';
+ }
+ else
+ {
+ $lang = $dir;
+ }
+ if ($lang eq 'plpython')
+ {
+ next
+ unless -d "$topdir/$Config/plpython3";
+ $lang = 'plpythonu';
+ }
+ else
+ {
+ next unless -d "$topdir/$Config/$lang";
+ }
+ my @lang_args = ("--load-extension=$lang");
+ chdir $dir;
+ my @tests = fetchTests();
+ if ($lang eq 'plperl')
+ {
+
+ # plperl tests will install the extensions themselves
+ @lang_args = ();
+
+ # assume we're using this perl to built postgres
+ # test if we can run two interpreters in one backend, and if so
+ # run the trusted/untrusted interaction tests
+ use Config;
+ if ($Config{usemultiplicity} eq 'define')
+ {
+ push(@tests, 'plperl_plperlu');
+ }
+ }
+ elsif ($lang eq 'plpythonu' && -d "$topdir/$Config/plpython3")
+ {
+ @lang_args = ();
+ }
+
+ # Move on if no tests are listed.
+ next if (scalar @tests == 0);
+
+ print
+ "============================================================\n";
+ print "Checking $lang\n";
+ my @args = (
+ "$topdir/$Config/pg_regress/pg_regress",
+ "--bindir=$topdir/$Config/psql",
+ "--dbname=pl_regression", @lang_args, @tests);
+ system(@args);
+ my $status = $? >> 8;
+ exit $status if $status;
+ chdir "$topdir/src/pl";
+ }
+
+ chdir "$topdir";
+ return;
+}
+
+sub subdircheck
+{
+ my $module = shift;
+
+ if ( !-d "$module/sql"
+ || !-d "$module/expected"
+ || (!-f "$module/GNUmakefile" && !-f "$module/Makefile"))
+ {
+ return;
+ }
+
+ chdir $module;
+ my @tests = fetchTests();
+
+ # Leave if no tests are listed in the module.
+ if (scalar @tests == 0)
+ {
+ chdir "..";
+ return;
+ }
+
+ my @opts = fetchRegressOpts();
+
+ print "============================================================\n";
+ print "Checking $module\n";
+ my @args = (
+ "$topdir/$Config/pg_regress/pg_regress",
+ "--bindir=${topdir}/${Config}/psql",
+ "--dbname=contrib_regression", @opts, @tests);
+ print join(' ', @args), "\n";
+ system(@args);
+ chdir "..";
+ return;
+}
+
+sub contribcheck
+{
+ chdir "../../../contrib";
+ my $mstat = 0;
+ foreach my $module (glob("*"))
+ {
+ # these configuration-based exclusions must match Install.pm
+ next if ($module eq "uuid-ossp" && !defined($config->{uuid}));
+ next if ($module eq "sslinfo" && !defined($config->{openssl}));
+ next if ($module eq "pgcrypto" && !defined($config->{openssl}));
+ next if ($module eq "xml2" && !defined($config->{xml}));
+ next if ($module =~ /_plperl$/ && !defined($config->{perl}));
+ next if ($module =~ /_plpython$/ && !defined($config->{python}));
+ next if ($module eq "sepgsql");
+
+ subdircheck($module);
+ my $status = $? >> 8;
+ $mstat ||= $status;
+ }
+ exit $mstat if $mstat;
+ return;
+}
+
+sub modulescheck
+{
+ chdir "../../../src/test/modules";
+ my $mstat = 0;
+ foreach my $module (glob("*"))
+ {
+ subdircheck($module);
+ my $status = $? >> 8;
+ $mstat ||= $status;
+ }
+ exit $mstat if $mstat;
+ return;
+}
+
+sub recoverycheck
+{
+ InstallTemp();
+
+ my $dir = "$topdir/src/test/recovery";
+ my $status = tap_check($dir);
+ exit $status if $status;
+ return;
+}
+
+# Run "initdb", then reconfigure authentication.
+sub standard_initdb
+{
+ return (
+ system('initdb', '-N') == 0 and system(
+ "$topdir/$Config/pg_regress/pg_regress", '--config-auth',
+ $ENV{PGDATA}) == 0);
+}
+
+# This is similar to appendShellString(). Perl system(@args) bypasses
+# cmd.exe, so omit the caret escape layer.
+sub quote_system_arg
+{
+ my $arg = shift;
+
+ # Change N >= 0 backslashes before a double quote to 2N+1 backslashes.
+ $arg =~ s/(\\*)"/${\($1 . $1)}\\"/gs;
+
+ # Change N >= 1 backslashes at end of argument to 2N backslashes.
+ $arg =~ s/(\\+)$/${\($1 . $1)}/gs;
+
+ # Wrap the whole thing in unescaped double quotes.
+ return "\"$arg\"";
+}
+
+sub upgradecheck
+{
+ # pg_upgrade is now handled by bincheck, but keep this target for
+ # backward compatibility.
+ print "upgradecheck is a no-op, use bincheck instead.\n";
+ return;
+}
+
+sub fetchRegressOpts
+{
+ my $handle;
+ open($handle, '<', "GNUmakefile")
+ || open($handle, '<', "Makefile")
+ || die "Could not open Makefile";
+ local ($/) = undef;
+ my $m = <$handle>;
+ close($handle);
+ my @opts;
+
+ $m =~ s{\\\r?\n}{}g;
+ if ($m =~ /^\s*REGRESS_OPTS\s*\+?=(.*)/m)
+ {
+
+ # Substitute known Makefile variables, then ignore options that retain
+ # an unhandled variable reference. Ignore anything that isn't an
+ # option starting with "--".
+ @opts = grep { !/\$\(/ && /^--/ }
+ map { (my $x = $_) =~ s/\Q$(top_builddir)\E/\"$topdir\"/; $x; }
+ split(/\s+/, $1);
+ }
+ if ($m =~ /^\s*ENCODING\s*=\s*(\S+)/m)
+ {
+ push @opts, "--encoding=$1";
+ }
+ if ($m =~ /^\s*NO_LOCALE\s*=\s*\S+/m)
+ {
+ push @opts, "--no-locale";
+ }
+ return @opts;
+}
+
+# Fetch the list of tests by parsing a module's Makefile. An empty
+# list is returned if the module does not need to run anything.
+sub fetchTests
+{
+ my $handle;
+ open($handle, '<', "GNUmakefile")
+ || open($handle, '<', "Makefile")
+ || die "Could not open Makefile";
+ local ($/) = undef;
+ my $m = <$handle>;
+ close($handle);
+ my $t = "";
+
+ $m =~ s{\\\r?\n}{}g;
+
+ # A module specifying NO_INSTALLCHECK does not support installcheck,
+ # so bypass its run by returning an empty set of tests.
+ if ($m =~ /^\s*NO_INSTALLCHECK\s*=\s*\S+/m)
+ {
+ return ();
+ }
+
+ if ($m =~ /^REGRESS\s*=\s*(.*)$/gm)
+ {
+ $t = $1;
+ $t =~ s/\s+/ /g;
+
+ if ($m =~ /contrib\/pgcrypto/)
+ {
+
+ # pgcrypto is special since some tests depend on the
+ # configuration of the build
+
+ my $pgptests =
+ $config->{zlib}
+ ? GetTests("ZLIB_TST", $m)
+ : GetTests("ZLIB_OFF_TST", $m);
+ $t =~ s/\$\(CF_PGP_TESTS\)/$pgptests/;
+ }
+ }
+
+ return split(/\s+/, $t);
+}
+
+sub GetTests
+{
+ my $testname = shift;
+ my $m = shift;
+ if ($m =~ /^$testname\s*=\s*(.*)$/gm)
+ {
+ return $1;
+ }
+ return "";
+}
+
+sub InstallTemp
+{
+ unless ($ENV{NO_TEMP_INSTALL})
+ {
+ print "Setting up temp install\n\n";
+ Install("$tmp_installdir", "all", $config);
+ }
+ $ENV{PATH} = "$tmp_installdir/bin;$ENV{PATH}";
+ return;
+}
+
+sub usage
+{
+ print STDERR
+ "Usage: vcregress.pl <mode> [<arg>]\n\n",
+ "Options for <mode>:\n",
+ " bincheck run tests of utilities in src/bin/\n",
+ " check deploy instance and run regression tests on it\n",
+ " contribcheck run tests of modules in contrib/\n",
+ " ecpgcheck run regression tests of ECPG\n",
+ " installcheck run regression tests on existing instance\n",
+ " isolationcheck run isolation tests\n",
+ " modulescheck run tests of modules in src/test/modules/\n",
+ " plcheck run tests of PL languages\n",
+ " recoverycheck run recovery test suite\n",
+ " taptest run an arbitrary TAP test set\n",
+ " upgradecheck run tests of pg_upgrade (no-op)\n",
+ "\nOptions for <arg>: (used by check and installcheck)\n",
+ " serial serial mode\n",
+ " parallel parallel mode\n",
+ "\nOption for <arg>: for taptest\n",
+ " TEST_DIR (required) directory where tests reside\n";
+ exit(1);
+}
diff --git a/src/tools/perlcheck/find_perl_files b/src/tools/perlcheck/find_perl_files
new file mode 100644
index 0000000..fd99dab
--- /dev/null
+++ b/src/tools/perlcheck/find_perl_files
@@ -0,0 +1,14 @@
+# src/tools/perlcheck/find_perl_files
+
+# shell function to find all perl files in the source tree
+
+find_perl_files () {
+ {
+ # take all .pl and .pm files
+ find . -type f -name '*.p[lm]' -print
+ # take executable files that file(1) thinks are perl files
+ find . -type f -perm -100 -exec file {} \; -print |
+ egrep -i ':.*perl[0-9]*\>' |
+ cut -d: -f1
+ } | sort -u | grep -v '^\./\.git/'
+}
diff --git a/src/tools/perlcheck/perlcriticrc b/src/tools/perlcheck/perlcriticrc
new file mode 100644
index 0000000..9267fb4
--- /dev/null
+++ b/src/tools/perlcheck/perlcriticrc
@@ -0,0 +1,31 @@
+######################################################################
+#
+# src/tools/perlcheck/perlcriticrc
+#
+# config file for perlcritic for Postgres project
+#
+#####################################################################
+
+severity = 5
+
+theme = core
+
+# print the policy name as well as the normal output
+verbose = %f: %m at line %l, column %c. %e. ([%p] Severity: %s)\n
+
+# Note: for policy descriptions see https://metacpan.org/release/Perl-Critic
+
+
+# allow octal constants with leading zeros
+[-ValuesAndExpressions::ProhibitLeadingZeros]
+
+# insist on use of the warnings pragma
+[TestingAndDebugging::RequireUseWarnings]
+severity = 5
+
+# forbid grep and map in void context
+[BuiltinFunctions::ProhibitVoidGrep]
+severity = 5
+
+[BuiltinFunctions::ProhibitVoidMap]
+severity = 5
diff --git a/src/tools/perlcheck/pgperlcritic b/src/tools/perlcheck/pgperlcritic
new file mode 100755
index 0000000..1c2f787
--- /dev/null
+++ b/src/tools/perlcheck/pgperlcritic
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# src/tools/perlcheck/pgperlcritic
+
+test -f src/tools/perlcheck/perlcriticrc || {
+ echo could not find src/tools/perlcheck/perlcriticrc
+ exit 1
+ }
+
+set -e
+
+# set this to override default perlcritic program:
+PERLCRITIC=${PERLCRITIC:-perlcritic}
+
+. src/tools/perlcheck/find_perl_files
+
+find_perl_files | xargs $PERLCRITIC \
+ --quiet \
+ --program-extensions .pl \
+ --profile=src/tools/perlcheck/perlcriticrc
diff --git a/src/tools/perlcheck/pgperlsyncheck b/src/tools/perlcheck/pgperlsyncheck
new file mode 100755
index 0000000..730f592
--- /dev/null
+++ b/src/tools/perlcheck/pgperlsyncheck
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# script to detect compile time errors and warnings in all perl files
+
+INCLUDES="-I src/tools/msvc -I src/tools/msvc/dummylib -I src/backend/catalog"
+INCLUDES="-I src/test/perl -I src/backend/utils/mb/Unicode $INCLUDES"
+INCLUDES="-I src/bin/pg_rewind -I src/test/ssl/t $INCLUDES"
+
+set -e
+
+. src/tools/perlcheck/find_perl_files
+
+# for zsh
+setopt shwordsplit 2>/dev/null || true
+
+find_perl_files | xargs -L 1 perl $INCLUDES -cw 2>&1 | grep -v OK
diff --git a/src/tools/pginclude/README b/src/tools/pginclude/README
new file mode 100644
index 0000000..49eb4b6
--- /dev/null
+++ b/src/tools/pginclude/README
@@ -0,0 +1,103 @@
+src/tools/pginclude/README
+
+pginclude
+=========
+
+These utilities help clean up #include file usage. They should be run
+in this order so that the include files have the proper includes before
+the C files are tested.
+
+pgfixinclude change #include's to <> or ""
+
+pgcompinclude [-v]
+ report which #include files can not compile on their own
+
+pgrminclude [-v]
+ remove extra #include's
+
+pgcheckdefines
+ check for #ifdef tests on symbols defined in files that
+ weren't included --- this is a necessary sanity check on
+ pgrminclude
+
+pgdefine create macro calls for all defines in the file (used by
+ the above routines)
+
+It is also a good idea to sort the pg-specific include files in
+alphabetic order. This is best done with a text editor. Typical usage
+order would be:
+
+ pgfixinclude
+ sort include references
+ run multiple times:
+ pgcompinclude
+ pgrminclude /src/include
+ pgrminclude /
+ pgcheckdefines
+
+There is a complexity when modifying /src/include. If include file 1
+includes file 2, and file 2 includes file 3, then when file 1 is
+processed, it needs only file 2, not file 3. However, if later, include
+file 2 is processed, and file 3 is not needed by file 2 and is removed,
+file 1 might then need to include file 3. For this reason, the
+pgcompinclude and pgrminclude /src/include steps must be run several
+times until all includes compile cleanly.
+
+Also, tests should be done with configure settings of --enable-cassert
+and EXEC_BACKEND on and off. It is also wise to test a WIN32 compile.
+
+Another tools that does a similar task is at:
+
+ http://code.google.com/p/include-what-you-use/
+
+An include file visualizer script is available at:
+
+ http://archives.postgresql.org/pgsql-hackers/2011-09/msg00311.php
+
+
+headerscheck
+============
+
+This script can be run to verify that all Postgres include files meet
+the project convention that they will compile "standalone", that is
+with no prerequisite headers other than postgres.h (or postgres_fe.h
+or c.h, as appropriate).
+
+A small number of header files are exempted from this requirement,
+and are skipped by the headerscheck script.
+
+The easy way to run the script is to say "make -s headerscheck" in
+the top-level build directory after completing a build. You should
+have included "--with-perl --with-python" in your configure options,
+else you're likely to get errors about related headers not being found.
+
+A limitation of the current script is that it doesn't know which headers
+are for frontend or backend, so it tests everything with postgres.h
+as prerequisite, even if postgres_fe.h would be more appropriate. Also
+note that the contents of macros are not checked; this is intentional.
+
+
+cpluspluscheck
+==============
+
+This script can be run to verify that all Postgres include files meet
+the project convention that they will compile as C++ code. Although
+the project's coding language is C, some people write extensions in C++,
+so it's helpful for include files to be C++-clean.
+
+A small number of header files are exempted from this requirement,
+and are skipped by the cpluspluscheck script.
+
+The easy way to run the script is to say "make -s cpluspluscheck" in
+the top-level build directory after completing a build. You should
+have included "--with-perl --with-python" in your configure options,
+else you're likely to get errors about related headers not being found.
+
+If you are using a non-g++-compatible C++ compiler, you may need to
+override the script's CXXFLAGS setting by setting a suitable environment
+value.
+
+A limitation of the current script is that it doesn't know which headers
+are for frontend or backend, so it tests everything with postgres.h
+as prerequisite, even if postgres_fe.h would be more appropriate. Also
+note that the contents of macros are not checked; this is intentional.
diff --git a/src/tools/pginclude/cpluspluscheck b/src/tools/pginclude/cpluspluscheck
new file mode 100755
index 0000000..42688ff
--- /dev/null
+++ b/src/tools/pginclude/cpluspluscheck
@@ -0,0 +1,175 @@
+#!/bin/sh
+
+# Check (almost) all PostgreSQL include files for C++ compatibility.
+#
+# Argument 1 is the top-level source directory, argument 2 the
+# top-level build directory (they might be the same). If not set, they
+# default to the current directory.
+#
+# Needs to be run after configuring and creating all generated headers.
+# It's advisable to configure --with-perl --with-python, else you're
+# likely to get errors from associated headers.
+#
+# No output if everything is OK, else compiler errors.
+#
+# src/tools/pginclude/cpluspluscheck
+# Copyright (c) 2009-2022, PostgreSQL Global Development Group
+
+if [ -z "$1" ]; then
+ srcdir="."
+else
+ srcdir="$1"
+fi
+
+if [ -z "$2" ]; then
+ builddir="."
+else
+ builddir="$2"
+fi
+
+me=`basename $0`
+
+# These switches are g++ specific, you may override if necessary.
+CXXFLAGS=${CXXFLAGS:- -fsyntax-only -Wall}
+
+# Pull some info from configure's results.
+MGLOB="$builddir/src/Makefile.global"
+CPPFLAGS=`sed -n 's/^CPPFLAGS[ ]*=[ ]*//p' "$MGLOB"`
+CXX=`sed -n 's/^CXX[ ]*=[ ]*//p' "$MGLOB"`
+perl_includespec=`sed -n 's/^perl_includespec[ ]*=[ ]*//p' "$MGLOB"`
+python_includespec=`sed -n 's/^python_includespec[ ]*=[ ]*//p' "$MGLOB"`
+
+# Extract any -I and -D switches from CPPFLAGS.
+# (If necessary, user can pass more switches by presetting EXTRAFLAGS.)
+for flag in $CPPFLAGS; do
+ case $flag in
+ -I*|-D*) EXTRAFLAGS="$EXTRAFLAGS $flag";;
+ esac
+done
+
+# Create temp directory.
+tmp=`mktemp -d /tmp/$me.XXXXXX`
+
+trap 'rm -rf $tmp' 0 1 2 3 15
+
+# Scan all of src/ and contrib/ for header files.
+for f in `cd "$srcdir" && find src contrib -name '*.h' -print`
+do
+ # Ignore files that are unportable or intentionally not standalone.
+
+ # These files are platform-specific, and c.h will include the
+ # one that's relevant for our current platform anyway.
+ test "$f" = src/include/port/aix.h && continue
+ test "$f" = src/include/port/cygwin.h && continue
+ test "$f" = src/include/port/darwin.h && continue
+ test "$f" = src/include/port/freebsd.h && continue
+ test "$f" = src/include/port/hpux.h && continue
+ test "$f" = src/include/port/linux.h && continue
+ test "$f" = src/include/port/netbsd.h && continue
+ test "$f" = src/include/port/openbsd.h && continue
+ test "$f" = src/include/port/solaris.h && continue
+ test "$f" = src/include/port/win32.h && continue
+
+ # Additional Windows-specific headers.
+ test "$f" = src/include/port/win32_port.h && continue
+ test "$f" = src/include/port/win32/sys/socket.h && continue
+ test "$f" = src/include/port/win32_msvc/dirent.h && continue
+ test "$f" = src/include/port/win32_msvc/utime.h && continue
+ test "$f" = src/include/port/win32ntdll.h && continue
+ test "$f" = src/port/pthread-win32.h && continue
+
+ # Likewise, these files are platform-specific, and the one
+ # relevant to our platform will be included by atomics.h.
+ test "$f" = src/include/port/atomics/arch-arm.h && continue
+ test "$f" = src/include/port/atomics/arch-hppa.h && continue
+ test "$f" = src/include/port/atomics/arch-ia64.h && continue
+ test "$f" = src/include/port/atomics/arch-ppc.h && continue
+ test "$f" = src/include/port/atomics/arch-x86.h && continue
+ test "$f" = src/include/port/atomics/fallback.h && continue
+ test "$f" = src/include/port/atomics/generic.h && continue
+ test "$f" = src/include/port/atomics/generic-acc.h && continue
+ test "$f" = src/include/port/atomics/generic-gcc.h && continue
+ test "$f" = src/include/port/atomics/generic-msvc.h && continue
+ test "$f" = src/include/port/atomics/generic-sunpro.h && continue
+
+ # rusagestub.h is also platform-specific, and will be included
+ # by utils/pg_rusage.h if necessary.
+ test "$f" = src/include/rusagestub.h && continue
+
+ # sepgsql.h depends on headers that aren't there on most platforms.
+ test "$f" = contrib/sepgsql/sepgsql.h && continue
+
+ # These files are not meant to be included standalone, because
+ # they contain lists that might have multiple use-cases.
+ test "$f" = src/include/access/rmgrlist.h && continue
+ test "$f" = src/include/parser/kwlist.h && continue
+ test "$f" = src/pl/plpgsql/src/pl_reserved_kwlist.h && continue
+ test "$f" = src/pl/plpgsql/src/pl_unreserved_kwlist.h && continue
+ test "$f" = src/interfaces/ecpg/preproc/c_kwlist.h && continue
+ test "$f" = src/interfaces/ecpg/preproc/ecpg_kwlist.h && continue
+ test "$f" = src/include/regex/regerrs.h && continue
+ test "$f" = src/include/tcop/cmdtaglist.h && continue
+ test "$f" = src/pl/plpgsql/src/plerrcodes.h && continue
+ test "$f" = src/pl/plpython/spiexceptions.h && continue
+ test "$f" = src/pl/tcl/pltclerrcodes.h && continue
+
+ # Also not meant to be included standalone.
+ test "$f" = src/include/common/unicode_combining_table.h && continue
+ test "$f" = src/include/common/unicode_east_asian_fw_table.h && continue
+
+ # We can't make these Bison output files compilable standalone
+ # without using "%code require", which old Bison versions lack.
+ # parser/gram.h will be included by parser/gramparse.h anyway.
+ test "$f" = src/include/parser/gram.h && continue
+ test "$f" = src/backend/parser/gram.h && continue
+ test "$f" = src/pl/plpgsql/src/pl_gram.h && continue
+ test "$f" = src/interfaces/ecpg/preproc/preproc.h && continue
+
+ # ppport.h is not under our control, so we can't make it standalone.
+ test "$f" = src/pl/plperl/ppport.h && continue
+
+ # regression.h is not actually C, but ECPG code.
+ test "$f" = src/interfaces/ecpg/test/regression.h && continue
+ # printf_hack.h produces "unused function" warnings.
+ test "$f" = src/interfaces/ecpg/test/printf_hack.h && continue
+
+ # pg_trace.h and utils/probes.h can include sys/sdt.h from SystemTap,
+ # which itself contains C++ code and so won't compile with a C++
+ # compiler under extern "C" linkage.
+ test "$f" = src/include/pg_trace.h && continue
+ test "$f" = src/include/utils/probes.h && continue
+
+ # pg_dump is not C++-clean because it uses "public" and "namespace"
+ # as field names, which is unfortunate but we won't change it now.
+ test "$f" = src/bin/pg_dump/compress_io.h && continue
+ test "$f" = src/bin/pg_dump/parallel.h && continue
+ test "$f" = src/bin/pg_dump/pg_backup_archiver.h && continue
+ test "$f" = src/bin/pg_dump/pg_dump.h && continue
+
+ # OK, create .c file to include this .h file.
+ {
+ echo 'extern "C" {'
+ test "$f" != src/include/postgres_fe.h && echo '#include "postgres.h"'
+ echo "#include \"$f\""
+ echo '};'
+ } >$tmp/test.cpp
+
+ # Some subdirectories need extra -I switches.
+ case "$f" in
+ src/pl/plperl/*)
+ EXTRAINCLUDES="$perl_includespec" ;;
+ src/pl/plpython/*)
+ EXTRAINCLUDES="$python_includespec" ;;
+ src/interfaces/ecpg/*)
+ EXTRAINCLUDES="-I $builddir/src/interfaces/ecpg/include -I $srcdir/src/interfaces/ecpg/include" ;;
+ *)
+ EXTRAINCLUDES="" ;;
+ esac
+
+ # Run the test.
+ ${CXX:-g++} -I $builddir -I $srcdir \
+ -I $builddir/src/include -I $srcdir/src/include \
+ -I $builddir/src/interfaces/libpq -I $srcdir/src/interfaces/libpq \
+ $EXTRAINCLUDES $EXTRAFLAGS $CXXFLAGS -c $tmp/test.cpp
+
+done
diff --git a/src/tools/pginclude/headerscheck b/src/tools/pginclude/headerscheck
new file mode 100755
index 0000000..f8b0674
--- /dev/null
+++ b/src/tools/pginclude/headerscheck
@@ -0,0 +1,159 @@
+#!/bin/sh
+
+# Check (almost) all PostgreSQL include files for standalone build.
+#
+# Argument 1 is the top-level source directory, argument 2 the
+# top-level build directory (they might be the same). If not set, they
+# default to the current directory.
+#
+# Needs to be run after configuring and creating all generated headers.
+# It's advisable to configure --with-perl --with-python, else you're
+# likely to get errors from associated headers.
+#
+# No output if everything is OK, else compiler errors.
+#
+# src/tools/pginclude/headerscheck
+# Copyright (c) 2009-2022, PostgreSQL Global Development Group
+
+if [ -z "$1" ]; then
+ srcdir="."
+else
+ srcdir="$1"
+fi
+
+if [ -z "$2" ]; then
+ builddir="."
+else
+ builddir="$2"
+fi
+
+me=`basename $0`
+
+# Pull some info from configure's results.
+MGLOB="$builddir/src/Makefile.global"
+CPPFLAGS=`sed -n 's/^CPPFLAGS[ ]*=[ ]*//p' "$MGLOB"`
+CFLAGS=`sed -n 's/^CFLAGS[ ]*=[ ]*//p' "$MGLOB"`
+CC=`sed -n 's/^CC[ ]*=[ ]*//p' "$MGLOB"`
+PG_SYSROOT=`sed -n 's/^PG_SYSROOT[ ]*=[ ]*//p' "$MGLOB"`
+perl_includespec=`sed -n 's/^perl_includespec[ ]*=[ ]*//p' "$MGLOB"`
+python_includespec=`sed -n 's/^python_includespec[ ]*=[ ]*//p' "$MGLOB"`
+
+# needed on Darwin
+CPPFLAGS=`echo "$CPPFLAGS" | sed "s|\\\$(PG_SYSROOT)|$PG_SYSROOT|g"`
+
+# (EXTRAFLAGS is not set here, but user can pass it in if need be.)
+
+# Create temp directory.
+tmp=`mktemp -d /tmp/$me.XXXXXX`
+
+trap 'rm -rf $tmp' 0 1 2 3 15
+
+# Scan all of src/ and contrib/ for header files.
+for f in `cd "$srcdir" && find src contrib -name '*.h' -print`
+do
+ # Ignore files that are unportable or intentionally not standalone.
+
+ # These files are platform-specific, and c.h will include the
+ # one that's relevant for our current platform anyway.
+ test "$f" = src/include/port/aix.h && continue
+ test "$f" = src/include/port/cygwin.h && continue
+ test "$f" = src/include/port/darwin.h && continue
+ test "$f" = src/include/port/freebsd.h && continue
+ test "$f" = src/include/port/hpux.h && continue
+ test "$f" = src/include/port/linux.h && continue
+ test "$f" = src/include/port/netbsd.h && continue
+ test "$f" = src/include/port/openbsd.h && continue
+ test "$f" = src/include/port/solaris.h && continue
+ test "$f" = src/include/port/win32.h && continue
+
+ # Additional Windows-specific headers.
+ test "$f" = src/include/port/win32_port.h && continue
+ test "$f" = src/include/port/win32/sys/socket.h && continue
+ test "$f" = src/include/port/win32_msvc/dirent.h && continue
+ test "$f" = src/include/port/win32_msvc/utime.h && continue
+ test "$f" = src/include/port/win32ntdll.h && continue
+ test "$f" = src/port/pthread-win32.h && continue
+
+ # Likewise, these files are platform-specific, and the one
+ # relevant to our platform will be included by atomics.h.
+ test "$f" = src/include/port/atomics/arch-arm.h && continue
+ test "$f" = src/include/port/atomics/arch-hppa.h && continue
+ test "$f" = src/include/port/atomics/arch-ia64.h && continue
+ test "$f" = src/include/port/atomics/arch-ppc.h && continue
+ test "$f" = src/include/port/atomics/arch-x86.h && continue
+ test "$f" = src/include/port/atomics/fallback.h && continue
+ test "$f" = src/include/port/atomics/generic.h && continue
+ test "$f" = src/include/port/atomics/generic-acc.h && continue
+ test "$f" = src/include/port/atomics/generic-gcc.h && continue
+ test "$f" = src/include/port/atomics/generic-msvc.h && continue
+ test "$f" = src/include/port/atomics/generic-sunpro.h && continue
+
+ # rusagestub.h is also platform-specific, and will be included
+ # by utils/pg_rusage.h if necessary.
+ test "$f" = src/include/rusagestub.h && continue
+
+ # sepgsql.h depends on headers that aren't there on most platforms.
+ test "$f" = contrib/sepgsql/sepgsql.h && continue
+
+ # These files are not meant to be included standalone, because
+ # they contain lists that might have multiple use-cases.
+ test "$f" = src/include/access/rmgrlist.h && continue
+ test "$f" = src/include/parser/kwlist.h && continue
+ test "$f" = src/pl/plpgsql/src/pl_reserved_kwlist.h && continue
+ test "$f" = src/pl/plpgsql/src/pl_unreserved_kwlist.h && continue
+ test "$f" = src/interfaces/ecpg/preproc/c_kwlist.h && continue
+ test "$f" = src/interfaces/ecpg/preproc/ecpg_kwlist.h && continue
+ test "$f" = src/include/regex/regerrs.h && continue
+ test "$f" = src/include/tcop/cmdtaglist.h && continue
+ test "$f" = src/pl/plpgsql/src/plerrcodes.h && continue
+ test "$f" = src/pl/plpython/spiexceptions.h && continue
+ test "$f" = src/pl/tcl/pltclerrcodes.h && continue
+
+ # Also not meant to be included standalone.
+ test "$f" = src/include/common/unicode_combining_table.h && continue
+ test "$f" = src/include/common/unicode_east_asian_fw_table.h && continue
+
+ # We can't make these Bison output files compilable standalone
+ # without using "%code require", which old Bison versions lack.
+ # parser/gram.h will be included by parser/gramparse.h anyway.
+ test "$f" = src/include/parser/gram.h && continue
+ test "$f" = src/backend/parser/gram.h && continue
+ test "$f" = src/pl/plpgsql/src/pl_gram.h && continue
+ test "$f" = src/interfaces/ecpg/preproc/preproc.h && continue
+
+ # This produces a "no previous prototype" warning.
+ test "$f" = src/include/storage/checksum_impl.h && continue
+
+ # ppport.h is not under our control, so we can't make it standalone.
+ test "$f" = src/pl/plperl/ppport.h && continue
+
+ # regression.h is not actually C, but ECPG code.
+ test "$f" = src/interfaces/ecpg/test/regression.h && continue
+ # printf_hack.h produces "unused function" warnings.
+ test "$f" = src/interfaces/ecpg/test/printf_hack.h && continue
+
+ # OK, create .c file to include this .h file.
+ {
+ test "$f" != src/include/postgres_fe.h && echo '#include "postgres.h"'
+ echo "#include \"$f\""
+ } >$tmp/test.c
+
+ # Some subdirectories need extra -I switches.
+ case "$f" in
+ src/pl/plperl/*)
+ EXTRAINCLUDES="$perl_includespec" ;;
+ src/pl/plpython/*)
+ EXTRAINCLUDES="$python_includespec" ;;
+ src/interfaces/ecpg/*)
+ EXTRAINCLUDES="-I $builddir/src/interfaces/ecpg/include -I $srcdir/src/interfaces/ecpg/include" ;;
+ *)
+ EXTRAINCLUDES="" ;;
+ esac
+
+ # Run the test.
+ ${CC:-gcc} $CPPFLAGS $CFLAGS -I $builddir -I $srcdir \
+ -I $builddir/src/include -I $srcdir/src/include \
+ -I $builddir/src/interfaces/libpq -I $srcdir/src/interfaces/libpq \
+ $EXTRAINCLUDES $EXTRAFLAGS -c $tmp/test.c -o $tmp/test.o
+
+done
diff --git a/src/tools/pginclude/pgcheckdefines b/src/tools/pginclude/pgcheckdefines
new file mode 100755
index 0000000..e46a756
--- /dev/null
+++ b/src/tools/pginclude/pgcheckdefines
@@ -0,0 +1,305 @@
+#! /usr/bin/perl
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+#
+# This script looks for symbols that are referenced in #ifdef or defined()
+# tests without having #include'd the file that defines them. Since this
+# situation won't necessarily lead to any compiler message, it seems worth
+# having an automated check for it. In particular, use this to audit the
+# results of pgrminclude!
+#
+# Usage: configure and build a PG source tree (non-VPATH), then start this
+# script at the top level. It's best to enable as many configure options
+# as you can, especially --enable-cassert which is known to affect include
+# requirements. NB: you MUST use gcc, unless you have another compiler that
+# can be persuaded to spit out the names of referenced include files.
+#
+# The results are necessarily platform-dependent, so use care in interpreting
+# them. We try to process all .c files, even those not intended for the
+# current platform, so there will be some phony failures.
+#
+# src/tools/pginclude/pgcheckdefines
+#
+
+use strict;
+use warnings;
+
+use Cwd;
+use File::Basename;
+
+my $topdir = cwd();
+
+# Programs to use
+my $FIND = "find";
+my $MAKE = "make";
+
+#
+# Build arrays of all the .c and .h files in the tree
+#
+# We ignore .h files under src/include/port/, since only the one exposed as
+# src/include/port.h is interesting. (XXX Windows ports have additional
+# files there?) Ditto for .h files in src/backend/port/ subdirectories.
+# Including these .h files would clutter the list of define'd symbols and
+# cause a lot of false-positive results.
+#
+my (@cfiles, @hfiles);
+
+open my $pipe, '-|', "$FIND * -type f -name '*.c'"
+ or die "can't fork: $!";
+while (<$pipe>)
+{
+ chomp;
+ push @cfiles, $_;
+}
+close $pipe or die "$FIND failed: $!";
+
+open $pipe, '-|', "$FIND * -type f -name '*.h'"
+ or die "can't fork: $!";
+while (<$pipe>)
+{
+ chomp;
+ push @hfiles, $_
+ unless m|^src/include/port/|
+ || m|^src/backend/port/\w+/|;
+}
+close $pipe or die "$FIND failed: $!";
+
+#
+# For each .h file, extract all the symbols it #define's, and add them to
+# a hash table. To cover the possibility of multiple .h files defining
+# the same symbol, we make each hash entry a hash of filenames.
+#
+my %defines;
+
+foreach my $hfile (@hfiles)
+{
+ open my $fh, '<', $hfile
+ or die "can't open $hfile: $!";
+ while (<$fh>)
+ {
+ if (m/^\s*#\s*define\s+(\w+)/)
+ {
+ $defines{$1}{$hfile} = 1;
+ }
+ }
+ close $fh;
+}
+
+#
+# For each file (both .h and .c), run the compiler to get a list of what
+# files it #include's. Then extract all the symbols it tests for defined-ness,
+# and check each one against the previously built hashtable.
+#
+foreach my $file (@hfiles, @cfiles)
+{
+ my ($fname, $fpath) = fileparse($file);
+ chdir $fpath or die "can't chdir to $fpath: $!";
+
+ #
+ # Ask 'make' to parse the makefile so we can get the correct flags to
+ # use. CPPFLAGS in particular varies for each subdirectory. If we are
+ # processing a .h file, we might be in a subdirectory that has no
+ # Makefile, in which case we have to fake it. Note that there seems
+ # no easy way to prevent make from recursing into subdirectories and
+ # hence printing multiple definitions --- we keep the last one, which
+ # should come from the current Makefile.
+ #
+ my $MAKECMD;
+
+ if (-f "Makefile" || -f "GNUmakefile")
+ {
+ $MAKECMD = "$MAKE -qp";
+ }
+ else
+ {
+ my $subdir = $fpath;
+ chop $subdir;
+ my $top_builddir = "..";
+ my $tmp = $fpath;
+ while (($tmp = dirname($tmp)) ne '.')
+ {
+ $top_builddir = $top_builddir . "/..";
+ }
+ $MAKECMD =
+ "$MAKE -qp 'subdir=$subdir' 'top_builddir=$top_builddir' -f '$top_builddir/src/Makefile.global'";
+ }
+
+ my ($CPPFLAGS, $CFLAGS, $CFLAGS_SL, $PTHREAD_CFLAGS, $CC);
+
+ open $pipe, '-|', "$MAKECMD"
+ or die "can't fork: $!";
+ while (<$pipe>)
+ {
+ if (m/^CPPFLAGS :?= (.*)/)
+ {
+ $CPPFLAGS = $1;
+ }
+ elsif (m/^CFLAGS :?= (.*)/)
+ {
+ $CFLAGS = $1;
+ }
+ elsif (m/^CFLAGS_SL :?= (.*)/)
+ {
+ $CFLAGS_SL = $1;
+ }
+ elsif (m/^PTHREAD_CFLAGS :?= (.*)/)
+ {
+ $PTHREAD_CFLAGS = $1;
+ }
+ elsif (m/^CC :?= (.*)/)
+ {
+ $CC = $1;
+ }
+ }
+
+ # If make exits with status 1, it's not an error, it just means make
+ # thinks some files may not be up-to-date. Only complain on status 2.
+ close PIPE;
+ die "$MAKE failed in $fpath\n" if $? != 0 && $? != 256;
+
+ # Expand out stuff that might be referenced in CFLAGS
+ $CFLAGS =~ s/\$\(CFLAGS_SL\)/$CFLAGS_SL/;
+ $CFLAGS =~ s/\$\(PTHREAD_CFLAGS\)/$PTHREAD_CFLAGS/;
+
+ #
+ # Run the compiler (which had better be gcc) to get the inclusions.
+ # "gcc -H" reports inclusions on stderr as "... filename" where the
+ # number of dots varies according to nesting depth.
+ #
+ my @includes = ();
+ my $COMPILE = "$CC $CPPFLAGS $CFLAGS -H -E $fname";
+ open $pipe, '-|', "$COMPILE 2>&1 >/dev/null"
+ or die "can't fork: $!";
+ while (<$pipe>)
+ {
+ if (m/^\.+ (.*)/)
+ {
+ my $include = $1;
+
+ # Ignore system headers (absolute paths); but complain if a
+ # .c file includes a system header before any PG header.
+ if ($include =~ m|^/|)
+ {
+ warn "$file includes $include before any Postgres inclusion\n"
+ if $#includes == -1 && $file =~ m/\.c$/;
+ next;
+ }
+
+ # Strip any "./" (assume this appears only at front)
+ $include =~ s|^\./||;
+
+ # Make path relative to top of tree
+ my $ipath = $fpath;
+ while ($include =~ s|^\.\./||)
+ {
+ $ipath = dirname($ipath) . "/";
+ }
+ $ipath =~ s|^\./||;
+ push @includes, $ipath . $include;
+ }
+ else
+ {
+ warn "$CC: $_";
+ }
+ }
+
+ # The compiler might fail, particularly if we are checking a file that's
+ # not supposed to be compiled at all on the current platform, so don't
+ # quit on nonzero status.
+ close PIPE or warn "$COMPILE failed in $fpath\n";
+
+ #
+ # Scan the file to find #ifdef, #ifndef, and #if defined() constructs
+ # We assume #ifdef isn't continued across lines, and that defined(foo)
+ # isn't split across lines either
+ #
+ open my $fh, '<', $fname
+ or die "can't open $file: $!";
+ my $inif = 0;
+ while (<$fh>)
+ {
+ my $line = $_;
+ if ($line =~ m/^\s*#\s*ifdef\s+(\w+)/)
+ {
+ checkit($file, $1, @includes);
+ }
+ if ($line =~ m/^\s*#\s*ifndef\s+(\w+)/)
+ {
+ checkit($file, $1, @includes);
+ }
+ if ($line =~ m/^\s*#\s*if\s+/)
+ {
+ $inif = 1;
+ }
+ if ($inif)
+ {
+ while ($line =~ s/\bdefined(\s+|\s*\(\s*)(\w+)//)
+ {
+ checkit($file, $2, @includes);
+ }
+ if (!($line =~ m/\\$/))
+ {
+ $inif = 0;
+ }
+ }
+ }
+ close $fh;
+
+ chdir $topdir or die "can't chdir to $topdir: $!";
+}
+
+exit 0;
+
+# Check an is-defined reference
+sub checkit
+{
+ my ($file, $symbol, @includes) = @_;
+
+ # Ignore if symbol isn't defined in any PG include files
+ if (!defined $defines{$symbol})
+ {
+ return;
+ }
+
+ #
+ # Try to match source(s) of symbol to the inclusions of the current file
+ # (including itself). We consider it OK if any one matches.
+ #
+ # Note: these tests aren't bulletproof; in theory the inclusion might
+ # occur after the use of the symbol. Given our normal file layout,
+ # however, the risk is minimal.
+ #
+ foreach my $deffile (keys %{ $defines{$symbol} })
+ {
+ return if $deffile eq $file;
+ foreach my $reffile (@includes)
+ {
+ return if $deffile eq $reffile;
+ }
+ }
+
+ #
+ # If current file is a .h file, it's OK for it to assume that one of the
+ # base headers (postgres.h or postgres_fe.h) has been included.
+ #
+ if ($file =~ m/\.h$/)
+ {
+ foreach my $deffile (keys %{ $defines{$symbol} })
+ {
+ return if $deffile eq 'src/include/c.h';
+ return if $deffile eq 'src/include/postgres.h';
+ return if $deffile eq 'src/include/postgres_fe.h';
+ return if $deffile eq 'src/include/pg_config.h';
+ return if $deffile eq 'src/include/pg_config_manual.h';
+ }
+ }
+
+ #
+ my @places = keys %{ $defines{$symbol} };
+ print "$file references $symbol, defined in @places\n";
+
+ # print "includes: @includes\n";
+
+ return;
+}
diff --git a/src/tools/pginclude/pgcompinclude b/src/tools/pginclude/pgcompinclude
new file mode 100755
index 0000000..12169db
--- /dev/null
+++ b/src/tools/pginclude/pgcompinclude
@@ -0,0 +1,47 @@
+:
+# report which #include files can not compile on their own
+# takes -v option to display compile failure message and line numbers
+# src/tools/pginclude/pgcompinclude
+
+: ${CC:=cc}
+: ${PGSRC:=src}
+
+if ! pgdefine
+then echo "pgdefine must be in your PATH" 1>&2
+ exit 1
+fi
+
+trap "rm -f /tmp/$$.c /tmp/$$.o /tmp/$$ /tmp/$$a" 0 1 2 3 15
+find . \( -name .git -a -prune \) -o -name '*.h' -type f -print | while read FILE
+do
+ sed 's/->[a-zA-Z0-9_\.]*//g' "$FILE" >/tmp/$$a
+ echo "#include \"postgres.h\"" >/tmp/$$.c
+
+ # suppress fcinfo errors
+ echo "struct {Datum arg[1];} *fcinfo;" >>/tmp/$$.c
+
+ echo "#include \"/tmp/$$a\"" >>/tmp/$$.c
+
+ echo "Datum include_test(void);" >>/tmp/$$.c
+ echo "Datum include_test() {" >>/tmp/$$.c
+
+ pgdefine "$FILE" >>/tmp/$$.c
+
+ echo "return (Datum)0;" >>/tmp/$$.c
+ echo "}" >>/tmp/$$.c
+
+ # Use -O1 to get warnings only generated by optimization,
+ # but -O2 is too slow.
+ $CC -fsyntax-only -Werror -Wall -Wmissing-prototypes \
+ -Wmissing-declarations -I$PGSRC/include -I$PGSRC/backend \
+ -I$PGSRC/interfaces/libpq -I`dirname $FILE` $CFLAGS -O1 -c /tmp/$$.c \
+ -o /tmp/$$.o >/tmp/$$ 2>&1
+ if [ "$?" -ne 0 ]
+ then echo "$FILE"
+ if [ "$1" = "-v" ]
+ then cat /tmp/$$
+ nl /tmp/$$.c
+ echo
+ fi
+ fi
+done
diff --git a/src/tools/pginclude/pgdefine b/src/tools/pginclude/pgdefine
new file mode 100755
index 0000000..242d035
--- /dev/null
+++ b/src/tools/pginclude/pgdefine
@@ -0,0 +1,25 @@
+:
+# create macro calls for all defines in the file
+
+# src/tools/pginclude/pgdefine
+
+trap "rm -f /tmp/$$" 0 1 2 3 15
+for FILE
+do
+ cat "$FILE" | grep "^#define" >/tmp/$$
+ cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*\)[ ][ ]*[^ ].*\\\\$/\1;/p'
+ cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*\)[ ][ ]*[^ ].*[^\\\\]$/(void)\1;/p'
+
+ (
+ cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*([^)]*)\).*\\\\$/\1;/p'
+ cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*([^)]*)\).*[^\\\\]$/(=void)\1;/p'
+ ) |
+ sed 's/([a-zA-Z0-9_ ][a-zA-Z0-9_ ]*)/(0)/g' |
+ sed 's/([a-zA-Z0-9_ ]*,/(0,/g' |
+ sed 's/,[a-zA-Z0-9_ ]*,/,0,/g' |
+ sed 's/,[a-zA-Z0-9_ ]*)/,0)/g' |
+ # do not cast 'return' macros as (void)
+ sed 's/(=void)\(.*return\)/\1/g' |
+ sed 's/(=void)\(.*RETURN\)/\1/g' |
+ sed 's/(=void)/(void)/g'
+done
diff --git a/src/tools/pginclude/pgfixinclude b/src/tools/pginclude/pgfixinclude
new file mode 100755
index 0000000..6721566
--- /dev/null
+++ b/src/tools/pginclude/pgfixinclude
@@ -0,0 +1,21 @@
+:
+# change #include's to <> or ""
+# src/tools/pginclude/pgfixinclude
+
+trap "rm -f /tmp/$$.c /tmp/$$.o /tmp/$$ /tmp/$$a /tmp/$$b" 0 1 2 3 15
+find . \( -name .git -a -prune \) -o -type f -name '*.[chyls]' -print |
+while read FILE
+do
+ cat "$FILE" | grep "^#include" |
+ sed 's/^#include[ ]*[<"]\([^>"]*\).*$/\1/g' |
+ while read INCLUDE
+ do
+ if [ -s /usr/include/"$INCLUDE" ]
+ then cat "$FILE" |
+ sed 's;^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]$;#include <'"$INCLUDE"'>;g' >/tmp/$$
+ else cat "$FILE" |
+ sed 's;^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]$;#include "'"$INCLUDE"'";g' >/tmp/$$
+ fi
+ cat /tmp/$$ > "$FILE"
+ done
+done
diff --git a/src/tools/pginclude/pgrminclude b/src/tools/pginclude/pgrminclude
new file mode 100755
index 0000000..7cbd2e7
--- /dev/null
+++ b/src/tools/pginclude/pgrminclude
@@ -0,0 +1,149 @@
+:
+# remove extra #include's
+
+# pgcompinclude must be run before and after pgrminclude. It must be
+# run before because we don't want include dependencies to leak into
+# the C program files, and after because removal of includes from headers
+# can cause new include unfulfilled dependencies.
+#
+# Limitations: 2011-09-24
+#
+# Pgrminclude, when processing header files, can cause includes to be
+# removed that require the addition of new illogical header files.
+# This is dependent on what order the header files are processed.
+# Manual review of header files now needed to satisfy pgcompinclude is
+# required.
+#
+# C program files that have #ifdef blocks that contain code that cannot
+# be compiled on the platform from which pgrminclude is run cannot be
+# processed, and are skipped.
+
+: ${CC:=cc}
+: ${PGSRC:=src}
+
+if ! pgdefine
+then echo "pgdefine must be in your PATH" 1>&2
+ exit 1
+fi
+
+trap "rm -f /tmp/$$.c /tmp/$$.o /tmp/$$ /tmp/$$a /tmp/$$b" 0 1 2 3 15
+
+if [ "$1" = "-v" ]
+then VERBOSE="Y"
+else VERBOSE=""
+fi
+
+verbose_output() {
+ if [ "$VERBOSE" ]
+ then cat /tmp/$$
+ cat /tmp/$$b
+ nl /tmp/$$.c
+ fi
+}
+
+process_includes_in_file() {
+ # loop through all includes mentioned in the file
+ cat "$FILE" |
+ grep "^#include\>" |
+ grep -v '/\* *pgrminclude *ignore *\*/' |
+ sed 's/^#include[ ]*[<"]\([^>"]*\).*$/\1/g' |
+ grep -v 'parser/kwlist\.h' |
+ grep -v '\.c$' |
+ while read INCLUDE
+ do if [ "$VERBOSE" ]
+ then echo "checking $FILE $INCLUDE"
+ fi
+ compile_file
+ done
+}
+
+compile_file() {
+ [ "$INCLUDE" -a -s /usr/include/"$INCLUDE" ] && continue
+ [ "$INCLUDE" = "postgres.h" ] && continue
+ [ "$INCLUDE" = "postgres_fe.h" ] && continue
+ [ "$INCLUDE" = "pg_config.h" ] && continue
+ [ "$INCLUDE" = "c.h" ] && continue
+ # Stringify macros will expand undefined identifiers, so skip files that use it
+ egrep -q '\<(CppAsString2?|CppConcat)\>' "$FILE" && continue
+
+ # preserve configure-specific includes
+ # these includes are surrounded by #ifdef's
+ grep -B1 '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' "$FILE" |
+ egrep -q '^#if|^#else|^#elif' && continue
+ grep -A1 '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' "$FILE" |
+ egrep -q '^#else|^#elif|^#endif' && continue
+
+ # Remove all #if and #ifdef blocks because the blocks
+ # might contain code that is not compiled on this platform.
+ cat "$FILE" |
+ grep -v "^#if" |
+ grep -v "^#else" |
+ grep -v "^#elif" |
+ grep -v "^#endif" |
+ # with #if blocks gone, now undef #defines to avoid redefine
+ # warning and failure
+ sed 's/#define[ ][ ]*\([A-Za-z0-9_]*\).*$/#undef \1\n&/' >/tmp/$$a
+
+ # set up initial file contents
+ grep -v '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' \
+ /tmp/$$a >/tmp/$$b
+
+ if [ "$IS_INCLUDE" = "Y" ]
+ then echo "#include \"postgres.h\"" >/tmp/$$.c
+ # suppress fcinfo errors
+ echo "struct {Datum arg[1];} *fcinfo;" >>/tmp/$$.c
+ else >/tmp/$$.c
+ fi
+
+ echo "#include \"/tmp/$$b\"" >>/tmp/$$.c
+
+ if [ "$IS_INCLUDE" = "Y" ]
+ then echo "Datum include_test(void);" >>/tmp/$$.c
+ echo "Datum include_test() {" >>/tmp/$$.c
+ pgdefine "$FILE" >>/tmp/$$.c
+ echo "return (Datum)0;" >>/tmp/$$.c
+ echo "}" >>/tmp/$$.c
+ fi
+
+ # Use -O1 to get warnings only generated by optimization,
+ # but -O2 is too slow.
+ $CC -fsyntax-only -Werror -Wall -Wmissing-prototypes \
+ -Wmissing-declarations -I$PGSRC/include -I$PGSRC/backend \
+ -I$PGSRC/interfaces/libpq -I`dirname $FILE` $CFLAGS -O1 -c /tmp/$$.c \
+ -o /tmp/$$.o >/tmp/$$ 2>&1
+ if [ "$?" -eq 0 ]
+ then [ "$INCLUDE" -o "$VERBOSE" ] && echo "$FILE $INCLUDE"
+ grep -v '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' \
+ "$FILE" >/tmp/$$b
+ mv /tmp/$$b "$FILE"
+ return 0
+ else return 1
+ fi
+}
+
+# Process include files first because they can affect the compilation
+# of *.c files.
+(find . \( -name .git -a -prune \) -o -type f -name '*.h' -print | sort;
+ find . \( -name .git -a -prune \) -o -type f -name '*.c' -print | sort) |
+grep -v '/postgres.h$' |
+grep -v '/postgres_fe.h$' |
+grep -v '/pg_config.h$' |
+grep -v '\./c.h$' |
+while read FILE
+do
+ if [ `expr $FILE : '.*\.h$'` -ne 0 ]
+ then IS_INCLUDE="Y"
+ else IS_INCLUDE="N"
+ fi
+
+ # Can we compile the file with all existing includes?
+ INCLUDE=""
+ compile_file
+ # If the file can't be compiled on its own, there is no sense
+ # trying to remove the include files.
+ if [ "$?" -ne 0 ]
+ then echo "cannot compile $FILE with existing includes"
+ verbose_output
+ else process_includes_in_file
+ fi
+done
diff --git a/src/tools/pgindent/README b/src/tools/pgindent/README
new file mode 100644
index 0000000..103970c
--- /dev/null
+++ b/src/tools/pgindent/README
@@ -0,0 +1,159 @@
+pgindent'ing the PostgreSQL source tree
+=======================================
+
+We run this process at least once in each development cycle,
+to maintain uniform layout style in our C and Perl code.
+
+You might find this blog post interesting:
+http://adpgtech.blogspot.com/2015/05/running-pgindent-on-non-core-code-or.html
+
+
+PREREQUISITES:
+
+1) Install pg_bsd_indent in your PATH. Fetch its source code with
+ git clone https://git.postgresql.org/git/pg_bsd_indent.git
+ then follow the directions in README.pg_bsd_indent therein.
+
+2) Install perltidy. Please be sure it is version 20170521 (older and newer
+ versions make different formatting choices, and we want consistency).
+ You can get the correct version from
+ https://cpan.metacpan.org/authors/id/S/SH/SHANCOCK/
+ To install, follow the usual install process for a Perl module
+ ("man perlmodinstall" explains it). Or, if you have cpan installed,
+ this should work:
+ cpan SHANCOCK/Perl-Tidy-20170521.tar.gz
+
+DOING THE INDENT RUN:
+
+1) Change directory to the top of the source tree.
+
+2) Download the latest typedef file from the buildfarm:
+
+ wget -O src/tools/pgindent/typedefs.list https://buildfarm.postgresql.org/cgi-bin/typedefs.pl
+
+ (See https://buildfarm.postgresql.org/cgi-bin/typedefs.pl?show_list for a full
+ list of typedef files, if you want to indent some back branch.)
+
+3) Run pgindent on the C files:
+
+ src/tools/pgindent/pgindent
+
+ If any files generate errors, restore their original versions with
+ "git checkout", and see below for cleanup ideas.
+
+4) Indent the Perl code using perltidy:
+
+ src/tools/pgindent/pgperltidy
+
+ If you want to use some perltidy version that's not in your PATH,
+ first set the PERLTIDY environment variable to point to it.
+
+5) Reformat the bootstrap catalog data files:
+
+ ./configure # "make" will not work in an unconfigured tree
+ cd src/include/catalog
+ make reformat-dat-files
+ cd ../../..
+
+VALIDATION:
+
+1) Check for any newly-created files using "git status"; there shouldn't
+ be any. (pgindent leaves *.BAK files behind if it has trouble, while
+ perltidy leaves *.LOG files behind.)
+
+2) Do a full test build:
+
+ make -s clean
+ make -s all # look for unexpected warnings, and errors of course
+ make check-world
+
+ Your configure switches should include at least --enable-tap-tests
+ or else much of the Perl code won't get exercised.
+ The ecpg regression tests may well fail due to pgindent's updates of
+ header files that get copied into ecpg output; if so, adjust the
+ expected-files to match.
+
+3) If you have the patience, it's worth eyeballing the "git diff" output
+ for any egregiously ugly changes. See below for cleanup ideas.
+
+
+When you're done, "git commit" everything including the typedefs.list file
+you used.
+
+4) Add the newly created commits to the .git-blame-ignore-revs file so
+ that "git blame" ignores the commits (for anybody that has opted-in
+ to using the ignore file). Follow the instructions that appear at
+ the top of the .git-blame-ignore-revs file.
+
+Another "git commit" will be required for your ignore file changes.
+
+---------------------------------------------------------------------------
+
+Cleaning up in case of failure or ugly output
+---------------------------------------------
+
+If you don't like the results for any particular file, "git checkout"
+that file to undo the changes, patch the file as needed, then repeat
+the indent process.
+
+pgindent will reflow any comment block that's not at the left margin.
+If this messes up manual formatting that ought to be preserved, protect
+the comment block with some dashes:
+
+ /*----------
+ * Text here will not be touched by pgindent.
+ *----------
+ */
+
+Odd spacing around typedef names might indicate an incomplete typedefs list.
+
+pgindent will mangle both declaration and definition of a C function whose
+name matches a typedef. Currently the best workaround is to choose
+non-conflicting names.
+
+pgindent can get confused by #if sequences that look correct to the compiler
+but have mismatched braces/parentheses when considered as a whole. Usually
+that looks pretty unreadable to humans too, so best practice is to rearrange
+the #if tests to avoid it.
+
+Sometimes, if pgindent or perltidy produces odd-looking output, it's because
+of minor bugs like extra commas. Don't hesitate to clean that up while
+you're at it.
+
+---------------------------------------------------------------------------
+
+BSD indent
+----------
+
+We have standardized on FreeBSD's indent, and renamed it pg_bsd_indent.
+pg_bsd_indent does differ slightly from FreeBSD's version, mostly in
+being more easily portable to non-BSD platforms. You can obtain it from
+https://git.postgresql.org/git/pg_bsd_indent.git
+
+GNU indent, version 2.2.6, has several problems, and is not recommended.
+These bugs become pretty major when you are doing >500k lines of code.
+If you don't believe me, take a directory and make a copy. Run pgindent
+on the copy using GNU indent, and do a diff -r. You will see what I
+mean. GNU indent does some things better, but mangles too. For details,
+see:
+
+ http://archives.postgresql.org/pgsql-hackers/2003-10/msg00374.php
+ http://archives.postgresql.org/pgsql-hackers/2011-04/msg01436.php
+
+---------------------------------------------------------------------------
+
+Which files are processed
+-------------------------
+
+The pgindent run processes (nearly) all PostgreSQL *.c and *.h files,
+but we currently exclude *.y and *.l files, as well as *.c and *.h files
+derived from *.y and *.l files. Additional exceptions are listed
+in exclude_file_patterns; see the notes therein for rationale.
+
+Note that we do not exclude ecpg's header files from the run. Some of them
+get copied verbatim into ecpg's output, meaning that ecpg's expected files
+may need to be updated to match.
+
+The perltidy run processes all *.pl and *.pm files, plus a few
+executable Perl scripts that are not named that way. See the "find"
+rules in pgperltidy for details.
diff --git a/src/tools/pgindent/exclude_file_patterns b/src/tools/pgindent/exclude_file_patterns
new file mode 100644
index 0000000..f08180b
--- /dev/null
+++ b/src/tools/pgindent/exclude_file_patterns
@@ -0,0 +1,49 @@
+# List of filename patterns to exclude from pgindent runs
+#
+# These contain assembly code that pgindent tends to mess up.
+src/include/storage/s_lock\.h$
+src/include/port/atomics/
+#
+# This contains C++ constructs that confuse pgindent.
+src/include/jit/llvmjit\.h$
+#
+# This confuses pgindent, and it's a derived file anyway.
+src/backend/utils/fmgrtab\.c$
+#
+# pgindent might mangle entries in this that match typedef names.
+# Since it's a derived file anyway, just exclude it.
+src/backend/utils/fmgrprotos\.h$
+#
+# kwlist_d files are made by gen_keywordlist.pl. While we could insist that
+# they match pgindent style, they'd look worse not better, so exclude them.
+kwlist_d\.h$
+#
+# These are generated by the scripts from src/common/unicode/. They use
+# hash functions generated by PerfectHash.pm whose format looks worse with
+# pgindent.
+src/include/common/unicode_norm_hashfunc\.h$
+src/include/common/unicode_normprops_table\.h$
+#
+# Exclude ecpg test files to avoid breaking the ecpg regression tests
+# (but include files at the top level of the ecpg/test/ directory).
+src/interfaces/ecpg/test/.*/
+#
+# src/include/snowball/libstemmer/ and src/backend/snowball/libstemmer/
+# are excluded because those files are imported from an external project,
+# rather than maintained locally, and they are machine-generated anyway.
+/snowball/libstemmer/
+#
+# These files are machine-generated by code not under our control,
+# so we shouldn't expect them to conform to our style.
+# (Some versions of dtrace build probes.h files that confuse pgindent, too.)
+src/backend/utils/probes\.h$
+src/include/pg_config\.h$
+src/pl/plperl/ppport\.h$
+src/pl/plperl/SPI\.c$
+src/pl/plperl/Util\.c$
+#
+# Exclude any temporary installations that may be in the tree.
+/tmp_check/
+/tmp_install/
+# ... and for paranoia's sake, don't touch git stuff.
+/\.git/
diff --git a/src/tools/pgindent/perltidyrc b/src/tools/pgindent/perltidyrc
new file mode 100644
index 0000000..9f09f0a
--- /dev/null
+++ b/src/tools/pgindent/perltidyrc
@@ -0,0 +1,16 @@
+--add-whitespace
+--backup-and-modify-in-place
+--backup-file-extension=/
+--delete-old-whitespace
+--entab-leading-whitespace=4
+--keep-old-blank-lines=2
+--maximum-line-length=78
+--nooutdent-long-comments
+--nooutdent-long-quotes
+--nospace-for-semicolon
+--opening-brace-on-new-line
+--output-line-ending=unix
+--paren-tightness=2
+--paren-vertical-tightness=2
+--paren-vertical-tightness-closing=2
+--noblanks-before-comments
diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent
new file mode 100755
index 0000000..2ef07bb
--- /dev/null
+++ b/src/tools/pgindent/pgindent
@@ -0,0 +1,436 @@
+#!/usr/bin/perl
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+use 5.008001;
+
+use Cwd qw(abs_path getcwd);
+use File::Find;
+use File::Spec qw(devnull);
+use File::Temp;
+use IO::Handle;
+use Getopt::Long;
+
+# Update for pg_bsd_indent version
+my $INDENT_VERSION = "2.1.1";
+
+# Our standard indent settings
+my $indent_opts =
+ "-bad -bap -bbb -bc -bl -cli1 -cp33 -cdb -nce -d0 -di12 -nfc1 -i4 -l79 -lp -lpl -nip -npro -sac -tpg -ts4";
+
+my $devnull = File::Spec->devnull;
+
+my ($typedefs_file, $typedef_str, $code_base, $excludes, $indent, $build);
+
+my %options = (
+ "typedefs=s" => \$typedefs_file,
+ "list-of-typedefs=s" => \$typedef_str,
+ "code-base=s" => \$code_base,
+ "excludes=s" => \$excludes,
+ "indent=s" => \$indent,
+ "build" => \$build,);
+GetOptions(%options) || die "bad command line argument\n";
+
+run_build($code_base) if ($build);
+
+# command line option wins, then first non-option arg,
+# then environment (which is how --build sets it) ,
+# then locations. based on current dir, then default location
+$typedefs_file ||= shift if @ARGV && $ARGV[0] !~ /\.[ch]$/;
+$typedefs_file ||= $ENV{PGTYPEDEFS};
+
+# build mode sets PGINDENT
+$indent ||= $ENV{PGINDENT} || $ENV{INDENT} || "pg_bsd_indent";
+
+# no non-option arguments given. so do everything in the current directory
+$code_base ||= '.' unless @ARGV;
+
+# if it's the base of a postgres tree, we will exclude the files
+# postgres wants excluded
+$excludes ||= "$code_base/src/tools/pgindent/exclude_file_patterns"
+ if $code_base && -f "$code_base/src/tools/pgindent/exclude_file_patterns";
+
+# The typedef list that's mechanically extracted by the buildfarm may omit
+# some names we want to treat like typedefs, e.g. "bool" (which is a macro
+# according to <stdbool.h>), and may include some names we don't want
+# treated as typedefs, although various headers that some builds include
+# might make them so. For the moment we just hardwire a list of names
+# to add and a list of names to exclude; eventually this may need to be
+# easier to configure. Note that the typedefs need trailing newlines.
+my @additional = ("bool\n");
+
+my %excluded = map { +"$_\n" => 1 } qw(
+ ANY FD_SET U abs allocfunc boolean date digit ilist interval iterator other
+ pointer printfunc reference string timestamp type wrap
+);
+
+# globals
+my @files;
+my $filtered_typedefs_fh;
+
+
+sub check_indent
+{
+ system("$indent -? < $devnull > $devnull 2>&1");
+ if ($? >> 8 != 1)
+ {
+ print STDERR
+ "You do not appear to have $indent installed on your system.\n";
+ exit 1;
+ }
+
+ if (`$indent --version` !~ m/ $INDENT_VERSION /)
+ {
+ print STDERR
+ "You do not appear to have $indent version $INDENT_VERSION installed on your system.\n";
+ exit 1;
+ }
+
+ system("$indent -gnu < $devnull > $devnull 2>&1");
+ if ($? == 0)
+ {
+ print STDERR
+ "You appear to have GNU indent rather than BSD indent.\n";
+ exit 1;
+ }
+
+ return;
+}
+
+
+sub load_typedefs
+{
+
+ # try fairly hard to find the typedefs file if it's not set
+
+ foreach my $try ('.', 'src/tools/pgindent', '/usr/local/etc')
+ {
+ $typedefs_file ||= "$try/typedefs.list"
+ if (-f "$try/typedefs.list");
+ }
+
+ # try to find typedefs by moving up directory levels
+ my $tdtry = "..";
+ foreach (1 .. 5)
+ {
+ $typedefs_file ||= "$tdtry/src/tools/pgindent/typedefs.list"
+ if (-f "$tdtry/src/tools/pgindent/typedefs.list");
+ $tdtry = "$tdtry/..";
+ }
+ die "cannot locate typedefs file \"$typedefs_file\"\n"
+ unless $typedefs_file && -f $typedefs_file;
+
+ open(my $typedefs_fh, '<', $typedefs_file)
+ || die "cannot open typedefs file \"$typedefs_file\": $!\n";
+ my @typedefs = <$typedefs_fh>;
+ close($typedefs_fh);
+
+ # add command-line-supplied typedefs?
+ if (defined($typedef_str))
+ {
+ foreach my $typedef (split(m/[, \t\n]+/, $typedef_str))
+ {
+ push(@typedefs, $typedef . "\n");
+ }
+ }
+
+ # add additional entries
+ push(@typedefs, @additional);
+
+ # remove excluded entries
+ @typedefs = grep { !$excluded{$_} } @typedefs;
+
+ # write filtered typedefs
+ my $filter_typedefs_fh = new File::Temp(TEMPLATE => "pgtypedefXXXXX");
+ print $filter_typedefs_fh @typedefs;
+ $filter_typedefs_fh->close();
+
+ # temp file remains because we return a file handle reference
+ return $filter_typedefs_fh;
+}
+
+
+sub process_exclude
+{
+ if ($excludes && @files)
+ {
+ open(my $eh, '<', $excludes)
+ || die "cannot open exclude file \"$excludes\"\n";
+ while (my $line = <$eh>)
+ {
+ chomp $line;
+ next if $line =~ m/^#/;
+ my $rgx = qr!$line!;
+ @files = grep { $_ !~ /$rgx/ } @files if $rgx;
+ }
+ close($eh);
+ }
+ return;
+}
+
+
+sub read_source
+{
+ my $source_filename = shift;
+ my $source;
+
+ open(my $src_fd, '<', $source_filename)
+ || die "cannot open file \"$source_filename\": $!\n";
+ local ($/) = undef;
+ $source = <$src_fd>;
+ close($src_fd);
+
+ return $source;
+}
+
+
+sub write_source
+{
+ my $source = shift;
+ my $source_filename = shift;
+
+ open(my $src_fh, '>', $source_filename)
+ || die "cannot open file \"$source_filename\": $!\n";
+ print $src_fh $source;
+ close($src_fh);
+ return;
+}
+
+
+sub pre_indent
+{
+ my $source = shift;
+
+ ## Comments
+
+ # Convert // comments to /* */
+ $source =~ s!^([ \t]*)//(.*)$!$1/* $2 */!gm;
+
+ # Adjust dash-protected block comments so indent won't change them
+ $source =~ s!/\* +---!/*---X_X!g;
+
+ ## Other
+
+ # Prevent indenting of code in 'extern "C"' blocks.
+ # we replace the braces with comments which we'll reverse later
+ my $extern_c_start = '/* Open extern "C" */';
+ my $extern_c_stop = '/* Close extern "C" */';
+ $source =~
+ s!(^#ifdef[ \t]+__cplusplus.*\nextern[ \t]+"C"[ \t]*\n)\{[ \t]*$!$1$extern_c_start!gm;
+ $source =~ s!(^#ifdef[ \t]+__cplusplus.*\n)\}[ \t]*$!$1$extern_c_stop!gm;
+
+ # Protect wrapping in CATALOG()
+ $source =~ s!^(CATALOG\(.*)$!/*$1*/!gm;
+
+ return $source;
+}
+
+
+sub post_indent
+{
+ my $source = shift;
+ my $source_filename = shift;
+
+ # Restore CATALOG lines
+ $source =~ s!^/\*(CATALOG\(.*)\*/$!$1!gm;
+
+ # Put back braces for extern "C"
+ $source =~ s!^/\* Open extern "C" \*/$!{!gm;
+ $source =~ s!^/\* Close extern "C" \*/$!}!gm;
+
+ ## Comments
+
+ # Undo change of dash-protected block comments
+ $source =~ s!/\*---X_X!/* ---!g;
+
+ # Fix run-together comments to have a tab between them
+ $source =~ s!\*/(/\*.*\*/)$!*/\t$1!gm;
+
+ ## Functions
+
+ # Use a single space before '*' in function return types
+ $source =~ s!^([A-Za-z_]\S*)[ \t]+\*$!$1 *!gm;
+
+ return $source;
+}
+
+
+sub run_indent
+{
+ my $source = shift;
+ my $error_message = shift;
+
+ my $cmd = "$indent $indent_opts -U" . $filtered_typedefs_fh->filename;
+
+ my $tmp_fh = new File::Temp(TEMPLATE => "pgsrcXXXXX");
+ my $filename = $tmp_fh->filename;
+ print $tmp_fh $source;
+ $tmp_fh->close();
+
+ $$error_message = `$cmd $filename 2>&1`;
+
+ return "" if ($? || length($$error_message) > 0);
+
+ unlink "$filename.BAK";
+
+ open(my $src_out, '<', $filename);
+ local ($/) = undef;
+ $source = <$src_out>;
+ close($src_out);
+
+ return $source;
+
+}
+
+
+# for development diagnostics
+sub diff
+{
+ my $pre = shift;
+ my $post = shift;
+ my $flags = shift || "";
+
+ print STDERR "running diff\n";
+
+ my $pre_fh = new File::Temp(TEMPLATE => "pgdiffbXXXXX");
+ my $post_fh = new File::Temp(TEMPLATE => "pgdiffaXXXXX");
+
+ print $pre_fh $pre;
+ print $post_fh $post;
+
+ $pre_fh->close();
+ $post_fh->close();
+
+ system( "diff $flags "
+ . $pre_fh->filename . " "
+ . $post_fh->filename
+ . " >&2");
+ return;
+}
+
+
+sub run_build
+{
+ eval "use LWP::Simple;"; ## no critic (ProhibitStringyEval);
+
+ my $code_base = shift || '.';
+ my $save_dir = getcwd();
+
+ # look for the code root
+ foreach (1 .. 5)
+ {
+ last if -d "$code_base/src/tools/pgindent";
+ $code_base = "$code_base/..";
+ }
+
+ die "cannot locate src/tools/pgindent directory in \"$code_base\"\n"
+ unless -d "$code_base/src/tools/pgindent";
+
+ chdir "$code_base/src/tools/pgindent";
+
+ my $typedefs_list_url =
+ "https://buildfarm.postgresql.org/cgi-bin/typedefs.pl";
+
+ my $rv = getstore($typedefs_list_url, "tmp_typedefs.list");
+
+ die "cannot fetch typedefs list from $typedefs_list_url\n"
+ unless is_success($rv);
+
+ $ENV{PGTYPEDEFS} = abs_path('tmp_typedefs.list');
+
+ my $indentrepo = "https://git.postgresql.org/git/pg_bsd_indent.git";
+ system("git clone $indentrepo >$devnull 2>&1");
+ die "could not fetch pg_bsd_indent sources from $indentrepo\n"
+ unless $? == 0;
+
+ chdir "pg_bsd_indent" || die;
+ system("make all check >$devnull");
+ die "could not build pg_bsd_indent from source\n"
+ unless $? == 0;
+
+ $ENV{PGINDENT} = abs_path('pg_bsd_indent');
+
+ chdir $save_dir;
+ return;
+}
+
+
+sub build_clean
+{
+ my $code_base = shift || '.';
+
+ # look for the code root
+ foreach (1 .. 5)
+ {
+ last if -d "$code_base/src/tools/pgindent";
+ $code_base = "$code_base/..";
+ }
+
+ die "cannot locate src/tools/pgindent directory in \"$code_base\"\n"
+ unless -d "$code_base/src/tools/pgindent";
+
+ chdir "$code_base";
+
+ system("rm -rf src/tools/pgindent/pg_bsd_indent");
+ system("rm -f src/tools/pgindent/tmp_typedefs.list");
+ return;
+}
+
+
+# main
+
+# get the list of files under code base, if it's set
+File::Find::find(
+ {
+ wanted => sub {
+ my ($dev, $ino, $mode, $nlink, $uid, $gid);
+ (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_))
+ && -f _
+ && /^.*\.[ch]\z/s
+ && push(@files, $File::Find::name);
+ }
+ },
+ $code_base) if $code_base;
+
+process_exclude();
+
+$filtered_typedefs_fh = load_typedefs();
+
+check_indent();
+
+# any non-option arguments are files to be processed
+push(@files, @ARGV);
+
+foreach my $source_filename (@files)
+{
+
+ # Automatically ignore .c and .h files that correspond to a .y or .l
+ # file. indent tends to get badly confused by Bison/flex output,
+ # and there's no value in indenting derived files anyway.
+ my $otherfile = $source_filename;
+ $otherfile =~ s/\.[ch]$/.y/;
+ next if $otherfile ne $source_filename && -f $otherfile;
+ $otherfile =~ s/\.y$/.l/;
+ next if $otherfile ne $source_filename && -f $otherfile;
+
+ my $source = read_source($source_filename);
+ my $orig_source = $source;
+ my $error_message = '';
+
+ $source = pre_indent($source);
+
+ $source = run_indent($source, \$error_message);
+ if ($source eq "")
+ {
+ print STDERR "Failure in $source_filename: " . $error_message . "\n";
+ next;
+ }
+
+ $source = post_indent($source, $source_filename);
+
+ write_source($source, $source_filename) if $source ne $orig_source;
+}
+
+build_clean($code_base) if $build;
diff --git a/src/tools/pgindent/pgindent.man b/src/tools/pgindent/pgindent.man
new file mode 100644
index 0000000..1c5aedd
--- /dev/null
+++ b/src/tools/pgindent/pgindent.man
@@ -0,0 +1,40 @@
+pgindent will indent .c and .h files according to the coding standards of
+the PostgreSQL project. It needs several things to run, and tries to locate
+or build them if possible. They can also be specified via command line switches
+or the environment.
+
+In its simplest form, if all the required objects are installed, simply run
+it without any parameters at the top of the source tree you want to process.
+
+ pgindent
+
+If you don't have all the requirements installed, pgindent will fetch and build
+them for you, if you're in a PostgreSQL source tree:
+
+ pgindent --build
+
+If your pg_bsd_indent program is not installed in your path, you can specify
+it by setting the environment variable INDENT, or PGINDENT, or by giving the
+command line option --indent:
+
+ pgindent --indent=/opt/extras/bsdindent
+
+pgindent also needs a file containing a list of typedefs. This can be
+specified using the PGTYPEDEFS environment variable, or via the command line
+--typedefs option. If neither is used, it will look for it within the
+current source tree, or in /usr/local/etc/typedefs.list.
+
+If you want to indent a source tree other than the current working directory,
+you can specify it via the --code-base command line option.
+
+We don't want to indent certain files in the PostgreSQL source. pgindent
+will honor a file containing a list of patterns of files to avoid. This
+file can be specified using the --excludes command line option. If indenting
+a PostgreSQL source tree, this option isn't necessary, as it will find the file
+src/tools/pgindent/exclude_file_patterns.
+
+Any non-option arguments are taken as the names of files to be indented. In this
+case only these files will be changed, and nothing else will be touched. If the
+first non-option argument is not a .c or .h file, it is treated as the name
+of a typedefs file for legacy reasons, but this use is deprecated - use the
+--typedefs option instead.
diff --git a/src/tools/pgindent/pgperltidy b/src/tools/pgindent/pgperltidy
new file mode 100755
index 0000000..5e70411
--- /dev/null
+++ b/src/tools/pgindent/pgperltidy
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# src/tools/pgindent/pgperltidy
+
+set -e
+
+# set this to override default perltidy program:
+PERLTIDY=${PERLTIDY:-perltidy}
+
+. src/tools/perlcheck/find_perl_files
+
+find_perl_files | xargs $PERLTIDY --profile=src/tools/pgindent/perltidyrc
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
new file mode 100644
index 0000000..826d5d6
--- /dev/null
+++ b/src/tools/pgindent/typedefs.list
@@ -0,0 +1,3900 @@
+ACCESS_ALLOWED_ACE
+ACL
+ACL_SIZE_INFORMATION
+AFFIX
+ASN1_INTEGER
+ASN1_OBJECT
+ASN1_OCTET_STRING
+ASN1_STRING
+AV
+A_ArrayExpr
+A_Const
+A_Expr
+A_Expr_Kind
+A_Indices
+A_Indirection
+A_Star
+AbsoluteTime
+AccessMethodInfo
+AccessPriv
+Acl
+AclItem
+AclMaskHow
+AclMode
+AclResult
+AcquireSampleRowsFunc
+ActionList
+ActiveSnapshotElt
+AddForeignUpdateTargets_function
+AffixNode
+AffixNodeData
+AfterTriggerEvent
+AfterTriggerEventChunk
+AfterTriggerEventData
+AfterTriggerEventList
+AfterTriggerShared
+AfterTriggerSharedData
+AfterTriggersData
+AfterTriggersQueryData
+AfterTriggersTableData
+AfterTriggersTransData
+Agg
+AggClauseCosts
+AggInfo
+AggPath
+AggSplit
+AggState
+AggStatePerAgg
+AggStatePerGroup
+AggStatePerHash
+AggStatePerPhase
+AggStatePerTrans
+AggStrategy
+AggTransInfo
+Aggref
+AggregateInstrumentation
+AlenState
+Alias
+AllocBlock
+AllocChunk
+AllocPointer
+AllocSet
+AllocSetContext
+AllocSetFreeList
+AllocateDesc
+AllocateDescKind
+AlterCollationStmt
+AlterDatabaseRefreshCollStmt
+AlterDatabaseSetStmt
+AlterDatabaseStmt
+AlterDefaultPrivilegesStmt
+AlterDomainStmt
+AlterEnumStmt
+AlterEventTrigStmt
+AlterExtensionContentsStmt
+AlterExtensionStmt
+AlterFdwStmt
+AlterForeignServerStmt
+AlterFunctionStmt
+AlterObjectDependsStmt
+AlterObjectSchemaStmt
+AlterOpFamilyStmt
+AlterOperatorStmt
+AlterOwnerStmt
+AlterPolicyStmt
+AlterPublicationAction
+AlterPublicationStmt
+AlterRoleSetStmt
+AlterRoleStmt
+AlterSeqStmt
+AlterStatsStmt
+AlterSubscriptionStmt
+AlterSubscriptionType
+AlterSystemStmt
+AlterTSConfigType
+AlterTSConfigurationStmt
+AlterTSDictionaryStmt
+AlterTableCmd
+AlterTableMoveAllStmt
+AlterTableSpaceOptionsStmt
+AlterTableStmt
+AlterTableType
+AlterTableUtilityContext
+AlterTypeRecurseParams
+AlterTypeStmt
+AlterUserMappingStmt
+AlteredTableInfo
+AlternativeSubPlan
+AmcheckOptions
+AnalyzeAttrComputeStatsFunc
+AnalyzeAttrFetchFunc
+AnalyzeForeignTable_function
+AnlExprData
+AnlIndexData
+AnyArrayType
+Append
+AppendPath
+AppendRelInfo
+AppendState
+ApplyErrorCallbackArg
+ApplyExecutionData
+ApplySubXactData
+Archive
+ArchiveCheckConfiguredCB
+ArchiveEntryPtrType
+ArchiveFileCB
+ArchiveFormat
+ArchiveHandle
+ArchiveMode
+ArchiveModuleCallbacks
+ArchiveModuleInit
+ArchiveOpts
+ArchiveShutdownCB
+ArchiveStreamState
+ArchiverOutput
+ArchiverStage
+ArrayAnalyzeExtraData
+ArrayBuildState
+ArrayBuildStateAny
+ArrayBuildStateArr
+ArrayCoerceExpr
+ArrayConstIterState
+ArrayExpr
+ArrayExprIterState
+ArrayIOData
+ArrayIterator
+ArrayMapState
+ArrayMetaState
+ArrayParseState
+ArraySubWorkspace
+ArrayType
+AsyncQueueControl
+AsyncQueueEntry
+AsyncRequest
+AttInMetadata
+AttStatsSlot
+AttoptCacheEntry
+AttoptCacheKey
+AttrDefInfo
+AttrDefault
+AttrMap
+AttrMissing
+AttrNumber
+AttributeOpts
+AuthRequest
+AuthToken
+AutoPrewarmSharedState
+AutoVacOpts
+AutoVacuumShmemStruct
+AutoVacuumWorkItem
+AutoVacuumWorkItemType
+AuxProcType
+BF_ctx
+BF_key
+BF_word
+BF_word_signed
+BIGNUM
+BIO
+BIO_METHOD
+BITVECP
+BMS_Comparison
+BMS_Membership
+BN_CTX
+BOOL
+BOOLEAN
+BOX
+BTArrayKeyInfo
+BTBuildState
+BTCycleId
+BTDedupInterval
+BTDedupState
+BTDedupStateData
+BTDeletedPageData
+BTIndexStat
+BTInsertState
+BTInsertStateData
+BTLeader
+BTMetaPageData
+BTOneVacInfo
+BTOptions
+BTPS_State
+BTPageOpaque
+BTPageOpaqueData
+BTPageStat
+BTPageState
+BTParallelScanDesc
+BTPendingFSM
+BTScanInsert
+BTScanInsertData
+BTScanOpaque
+BTScanOpaqueData
+BTScanPos
+BTScanPosData
+BTScanPosItem
+BTShared
+BTSortArrayContext
+BTSpool
+BTStack
+BTStackData
+BTVacInfo
+BTVacState
+BTVacuumPosting
+BTVacuumPostingData
+BTWriteState
+BUF_MEM
+BYTE
+BY_HANDLE_FILE_INFORMATION
+Backend
+BackendId
+BackendParameters
+BackendState
+BackendType
+BackgroundWorker
+BackgroundWorkerArray
+BackgroundWorkerHandle
+BackgroundWorkerSlot
+Barrier
+BaseBackupCmd
+BaseBackupTargetHandle
+BaseBackupTargetType
+BeginDirectModify_function
+BeginForeignInsert_function
+BeginForeignModify_function
+BeginForeignScan_function
+BeginSampleScan_function
+BernoulliSamplerData
+BgWorkerStartTime
+BgwHandleStatus
+BinaryArithmFunc
+BindParamCbData
+BipartiteMatchState
+BitString
+BitmapAnd
+BitmapAndPath
+BitmapAndState
+BitmapHeapPath
+BitmapHeapScan
+BitmapHeapScanState
+BitmapIndexScan
+BitmapIndexScanState
+BitmapOr
+BitmapOrPath
+BitmapOrState
+Bitmapset
+BlobInfo
+Block
+BlockId
+BlockIdData
+BlockInfoRecord
+BlockNumber
+BlockSampler
+BlockSamplerData
+BlockedProcData
+BlockedProcsData
+BloomBuildState
+BloomFilter
+BloomMetaPageData
+BloomOpaque
+BloomOptions
+BloomPageOpaque
+BloomPageOpaqueData
+BloomScanOpaque
+BloomScanOpaqueData
+BloomSignatureWord
+BloomState
+BloomTuple
+BoolAggState
+BoolExpr
+BoolExprType
+BoolTestType
+Boolean
+BooleanTest
+BpChar
+BrinBuildState
+BrinDesc
+BrinMemTuple
+BrinMetaPageData
+BrinOpaque
+BrinOpcInfo
+BrinOptions
+BrinRevmap
+BrinSpecialSpace
+BrinStatsData
+BrinTuple
+BrinValues
+BtreeCheckState
+BtreeLevel
+Bucket
+BufFile
+Buffer
+BufferAccessStrategy
+BufferAccessStrategyType
+BufferCachePagesContext
+BufferCachePagesRec
+BufferDesc
+BufferDescPadded
+BufferHeapTupleTableSlot
+BufferLookupEnt
+BufferStrategyControl
+BufferTag
+BufferUsage
+BuildAccumulator
+BuiltinScript
+BulkInsertState
+BulkInsertStateData
+CACHESIGN
+CAC_state
+CCFastEqualFN
+CCHashFN
+CEOUC_WAIT_MODE
+CFuncHashTabEntry
+CHAR
+CHECKPOINT
+CHKVAL
+CIRCLE
+CMPDAffix
+CONTEXT
+COP
+CRITICAL_SECTION
+CRSSnapshotAction
+CState
+CTECycleClause
+CTEMaterialize
+CTESearchClause
+CV
+CachedExpression
+CachedPlan
+CachedPlanSource
+CallContext
+CallStmt
+CancelRequestPacket
+Cardinality
+CaseExpr
+CaseTestExpr
+CaseWhen
+Cash
+CastInfo
+CatCList
+CatCTup
+CatCache
+CatCacheHeader
+CatalogId
+CatalogIdMapEntry
+CatalogIndexState
+ChangeVarNodes_context
+CheckPoint
+CheckPointStmt
+CheckpointStatsData
+CheckpointerRequest
+CheckpointerShmemStruct
+Chromosome
+CkptSortItem
+CkptTsStatus
+ClientAuthentication_hook_type
+ClientCertMode
+ClientCertName
+ClientData
+ClonePtrType
+ClosePortalStmt
+ClosePtrType
+Clump
+ClusterInfo
+ClusterParams
+ClusterStmt
+CmdType
+CoalesceExpr
+CoerceParamHook
+CoerceToDomain
+CoerceToDomainValue
+CoerceViaIO
+CoercionContext
+CoercionForm
+CoercionPathType
+CollAliasData
+CollInfo
+CollateClause
+CollateExpr
+CollateStrength
+CollectedATSubcmd
+CollectedCommand
+CollectedCommandType
+ColorTrgm
+ColorTrgmInfo
+ColumnCompareData
+ColumnDef
+ColumnIOData
+ColumnRef
+ColumnsHashData
+CombinationGenerator
+ComboCidEntry
+ComboCidEntryData
+ComboCidKey
+ComboCidKeyData
+Command
+CommandDest
+CommandId
+CommandTag
+CommandTagBehavior
+CommentItem
+CommentStmt
+CommitTimestampEntry
+CommitTimestampShared
+CommonEntry
+CommonTableExpr
+CompareScalarsContext
+CompiledExprState
+CompositeIOData
+CompositeTypeStmt
+CompoundAffixFlag
+CompressionAlgorithm
+CompressionLocation
+CompressorState
+ComputeXidHorizonsResult
+ConditionVariable
+ConditionVariableMinimallyPadded
+ConditionalStack
+ConfigData
+ConfigVariable
+ConnCacheEntry
+ConnCacheKey
+ConnParams
+ConnStatusType
+ConnType
+ConnectionStateEnum
+ConsiderSplitContext
+Const
+ConstrCheck
+ConstrType
+Constraint
+ConstraintCategory
+ConstraintInfo
+ConstraintsSetStmt
+ControlData
+ControlFileData
+ConvInfo
+ConvProcInfo
+ConversionLocation
+ConvertRowtypeExpr
+CookedConstraint
+CopyDest
+CopyFormatOptions
+CopyFromState
+CopyFromStateData
+CopyHeaderChoice
+CopyInsertMethod
+CopyMultiInsertBuffer
+CopyMultiInsertInfo
+CopySource
+CopyStmt
+CopyToState
+CopyToStateData
+Cost
+CostSelector
+Counters
+CoverExt
+CoverPos
+CreateAmStmt
+CreateCastStmt
+CreateConversionStmt
+CreateDBRelInfo
+CreateDBStrategy
+CreateDomainStmt
+CreateEnumStmt
+CreateEventTrigStmt
+CreateExtensionStmt
+CreateFdwStmt
+CreateForeignServerStmt
+CreateForeignTableStmt
+CreateFunctionStmt
+CreateOpClassItem
+CreateOpClassStmt
+CreateOpFamilyStmt
+CreatePLangStmt
+CreatePolicyStmt
+CreatePublicationStmt
+CreateRangeStmt
+CreateReplicationSlotCmd
+CreateRoleStmt
+CreateSchemaStmt
+CreateSchemaStmtContext
+CreateSeqStmt
+CreateStatsStmt
+CreateStmt
+CreateStmtContext
+CreateSubscriptionStmt
+CreateTableAsStmt
+CreateTableSpaceStmt
+CreateTransformStmt
+CreateTrigStmt
+CreateUserMappingStmt
+CreatedbStmt
+CredHandle
+CteItem
+CteScan
+CteScanState
+CteState
+CtlCommand
+CtxtHandle
+CurrentOfExpr
+CustomExecMethods
+CustomOutPtrType
+CustomPath
+CustomScan
+CustomScanMethods
+CustomScanState
+CycleCtr
+DBState
+DCHCacheEntry
+DEADLOCK_INFO
+DECountItem
+DH
+DIR
+DNSServiceErrorType
+DNSServiceRef
+DR_copy
+DR_intorel
+DR_printtup
+DR_sqlfunction
+DR_transientrel
+DSA
+DWORD
+DataDumperPtr
+DataPageDeleteStack
+DatabaseInfo
+DateADT
+Datum
+DatumTupleFields
+DbInfo
+DbInfoArr
+DeClonePtrType
+DeadLockState
+DeallocateStmt
+DeclareCursorStmt
+DecodedBkpBlock
+DecodedXLogRecord
+DecodingOutputState
+DefElem
+DefElemAction
+DefaultACLInfo
+DefineStmt
+DeleteStmt
+DependencyGenerator
+DependencyGeneratorData
+DependencyType
+DestReceiver
+DictISpell
+DictInt
+DictSimple
+DictSnowball
+DictSubState
+DictSyn
+DictThesaurus
+DimensionInfo
+DirectoryMethodData
+DirectoryMethodFile
+DisableTimeoutParams
+DiscardMode
+DiscardStmt
+DistanceValue
+DistinctExpr
+DoStmt
+DocRepresentation
+DomainConstraintCache
+DomainConstraintRef
+DomainConstraintState
+DomainConstraintType
+DomainIOData
+DropBehavior
+DropOwnedStmt
+DropReplicationSlotCmd
+DropRoleStmt
+DropStmt
+DropSubscriptionStmt
+DropTableSpaceStmt
+DropUserMappingStmt
+DropdbStmt
+DumpComponents
+DumpId
+DumpOptions
+DumpSignalInformation
+DumpableAcl
+DumpableObject
+DumpableObjectType
+DumpableObjectWithAcl
+DynamicFileList
+DynamicZoneAbbrev
+EC_KEY
+EDGE
+ENGINE
+EOM_flatten_into_method
+EOM_get_flat_size_method
+EPQState
+EPlan
+EState
+EStatus
+EVP_CIPHER
+EVP_CIPHER_CTX
+EVP_MD
+EVP_MD_CTX
+EVP_PKEY
+EachState
+Edge
+EditableObjectType
+ElementsState
+EnableTimeoutParams
+EndBlobPtrType
+EndBlobsPtrType
+EndDataPtrType
+EndDirectModify_function
+EndForeignInsert_function
+EndForeignModify_function
+EndForeignScan_function
+EndOfWalRecoveryInfo
+EndSampleScan_function
+EnumItem
+EolType
+EphemeralNameRelationType
+EphemeralNamedRelation
+EphemeralNamedRelationData
+EphemeralNamedRelationMetadata
+EphemeralNamedRelationMetadataData
+EquivalenceClass
+EquivalenceMember
+ErrorContextCallback
+ErrorData
+EstimateDSMForeignScan_function
+EstimationInfo
+EventTriggerCacheEntry
+EventTriggerCacheItem
+EventTriggerCacheStateType
+EventTriggerData
+EventTriggerEvent
+EventTriggerInfo
+EventTriggerQueryState
+ExceptionLabelMap
+ExceptionMap
+ExecAuxRowMark
+ExecEvalBoolSubroutine
+ExecEvalJsonExprContext
+ExecEvalSubroutine
+ExecForeignBatchInsert_function
+ExecForeignDelete_function
+ExecForeignInsert_function
+ExecForeignTruncate_function
+ExecForeignUpdate_function
+ExecParallelEstimateContext
+ExecParallelInitializeDSMContext
+ExecPhraseData
+ExecProcNodeMtd
+ExecRowMark
+ExecScanAccessMtd
+ExecScanRecheckMtd
+ExecStatus
+ExecStatusType
+ExecuteStmt
+ExecutorCheckPerms_hook_type
+ExecutorEnd_hook_type
+ExecutorFinish_hook_type
+ExecutorRun_hook_type
+ExecutorStart_hook_type
+ExpandedArrayHeader
+ExpandedObjectHeader
+ExpandedObjectMethods
+ExpandedRange
+ExpandedRecordFieldInfo
+ExpandedRecordHeader
+ExplainDirectModify_function
+ExplainForeignModify_function
+ExplainForeignScan_function
+ExplainFormat
+ExplainOneQuery_hook_type
+ExplainState
+ExplainStmt
+ExplainWorkersState
+ExportedSnapshot
+Expr
+ExprContext
+ExprContextCallbackFunction
+ExprContext_CB
+ExprDoneCond
+ExprEvalOp
+ExprEvalOpLookup
+ExprEvalRowtypeCache
+ExprEvalStep
+ExprState
+ExprStateEvalFunc
+ExtensibleNode
+ExtensibleNodeEntry
+ExtensibleNodeMethods
+ExtensionControlFile
+ExtensionInfo
+ExtensionVersionInfo
+FDWCollateState
+FD_SET
+FILE
+FILETIME
+FPI
+FSMAddress
+FSMPage
+FSMPageData
+FakeRelCacheEntry
+FakeRelCacheEntryData
+FastPathStrongRelationLockData
+FdwInfo
+FdwRoutine
+FetchDirection
+FetchStmt
+FieldSelect
+FieldStore
+File
+FileFdwExecutionState
+FileFdwPlanState
+FileNameMap
+FileSet
+FileTag
+FinalPathExtraData
+FindColsContext
+FindSplitData
+FindSplitStrat
+FixedParallelExecutorState
+FixedParallelState
+FixedParamState
+FlagMode
+Float
+FlushPosition
+FmgrBuiltin
+FmgrHookEventType
+FmgrInfo
+ForBothCellState
+ForBothState
+ForEachState
+ForFiveState
+ForFourState
+ForThreeState
+ForeignAsyncConfigureWait_function
+ForeignAsyncNotify_function
+ForeignAsyncRequest_function
+ForeignDataWrapper
+ForeignKeyCacheInfo
+ForeignKeyOptInfo
+ForeignPath
+ForeignScan
+ForeignScanState
+ForeignServer
+ForeignServerInfo
+ForeignTable
+ForeignTruncateInfo
+ForkNumber
+FormData_pg_aggregate
+FormData_pg_am
+FormData_pg_amop
+FormData_pg_amproc
+FormData_pg_attrdef
+FormData_pg_attribute
+FormData_pg_auth_members
+FormData_pg_authid
+FormData_pg_cast
+FormData_pg_class
+FormData_pg_collation
+FormData_pg_constraint
+FormData_pg_conversion
+FormData_pg_database
+FormData_pg_default_acl
+FormData_pg_depend
+FormData_pg_enum
+FormData_pg_event_trigger
+FormData_pg_extension
+FormData_pg_foreign_data_wrapper
+FormData_pg_foreign_server
+FormData_pg_foreign_table
+FormData_pg_index
+FormData_pg_inherits
+FormData_pg_language
+FormData_pg_largeobject
+FormData_pg_largeobject_metadata
+FormData_pg_namespace
+FormData_pg_opclass
+FormData_pg_operator
+FormData_pg_opfamily
+FormData_pg_partitioned_table
+FormData_pg_policy
+FormData_pg_proc
+FormData_pg_publication
+FormData_pg_publication_namespace
+FormData_pg_publication_rel
+FormData_pg_range
+FormData_pg_replication_origin
+FormData_pg_rewrite
+FormData_pg_sequence
+FormData_pg_sequence_data
+FormData_pg_shdepend
+FormData_pg_statistic
+FormData_pg_statistic_ext
+FormData_pg_statistic_ext_data
+FormData_pg_subscription
+FormData_pg_subscription_rel
+FormData_pg_tablespace
+FormData_pg_transform
+FormData_pg_trigger
+FormData_pg_ts_config
+FormData_pg_ts_config_map
+FormData_pg_ts_dict
+FormData_pg_ts_parser
+FormData_pg_ts_template
+FormData_pg_type
+FormData_pg_user_mapping
+Form_pg_aggregate
+Form_pg_am
+Form_pg_amop
+Form_pg_amproc
+Form_pg_attrdef
+Form_pg_attribute
+Form_pg_auth_members
+Form_pg_authid
+Form_pg_cast
+Form_pg_class
+Form_pg_collation
+Form_pg_constraint
+Form_pg_conversion
+Form_pg_database
+Form_pg_default_acl
+Form_pg_depend
+Form_pg_enum
+Form_pg_event_trigger
+Form_pg_extension
+Form_pg_foreign_data_wrapper
+Form_pg_foreign_server
+Form_pg_foreign_table
+Form_pg_index
+Form_pg_inherits
+Form_pg_language
+Form_pg_largeobject
+Form_pg_largeobject_metadata
+Form_pg_namespace
+Form_pg_opclass
+Form_pg_operator
+Form_pg_opfamily
+Form_pg_partitioned_table
+Form_pg_policy
+Form_pg_proc
+Form_pg_publication
+Form_pg_publication_namespace
+Form_pg_publication_rel
+Form_pg_range
+Form_pg_replication_origin
+Form_pg_rewrite
+Form_pg_sequence
+Form_pg_sequence_data
+Form_pg_shdepend
+Form_pg_statistic
+Form_pg_statistic_ext
+Form_pg_statistic_ext_data
+Form_pg_subscription
+Form_pg_subscription_rel
+Form_pg_tablespace
+Form_pg_transform
+Form_pg_trigger
+Form_pg_ts_config
+Form_pg_ts_config_map
+Form_pg_ts_dict
+Form_pg_ts_parser
+Form_pg_ts_template
+Form_pg_type
+Form_pg_user_mapping
+FormatNode
+FreeBlockNumberArray
+FreeListData
+FreePageBtree
+FreePageBtreeHeader
+FreePageBtreeInternalKey
+FreePageBtreeLeafKey
+FreePageBtreeSearchResult
+FreePageManager
+FreePageSpanLeader
+FromCharDateMode
+FromExpr
+FullTransactionId
+FuncCall
+FuncCallContext
+FuncCandidateList
+FuncDetailCode
+FuncExpr
+FuncInfo
+FuncLookupError
+FunctionCallInfo
+FunctionCallInfoBaseData
+FunctionParameter
+FunctionParameterMode
+FunctionScan
+FunctionScanPerFuncState
+FunctionScanState
+FuzzyAttrMatchState
+GBT_NUMKEY
+GBT_NUMKEY_R
+GBT_VARKEY
+GBT_VARKEY_R
+GENERAL_NAME
+GISTBuildBuffers
+GISTBuildState
+GISTDeletedPageContents
+GISTENTRY
+GISTInsertStack
+GISTInsertState
+GISTIntArrayBigOptions
+GISTIntArrayOptions
+GISTNodeBuffer
+GISTNodeBufferPage
+GISTPageOpaque
+GISTPageOpaqueData
+GISTPageSplitInfo
+GISTSTATE
+GISTScanOpaque
+GISTScanOpaqueData
+GISTSearchHeapItem
+GISTSearchItem
+GISTTYPE
+GIST_SPLITVEC
+GMReaderTupleBuffer
+GROUP
+GV
+Gather
+GatherMerge
+GatherMergePath
+GatherMergeState
+GatherPath
+GatherState
+Gene
+GeneratePruningStepsContext
+GenerationBlock
+GenerationChunk
+GenerationContext
+GenerationPointer
+GenericCosts
+GenericXLogState
+GeqoPrivateData
+GetForeignJoinPaths_function
+GetForeignModifyBatchSize_function
+GetForeignPaths_function
+GetForeignPlan_function
+GetForeignRelSize_function
+GetForeignRowMarkType_function
+GetForeignUpperPaths_function
+GetState
+GiSTOptions
+GinBtree
+GinBtreeData
+GinBtreeDataLeafInsertData
+GinBtreeEntryInsertData
+GinBtreeStack
+GinBuildState
+GinChkVal
+GinEntries
+GinEntryAccumulator
+GinIndexStat
+GinMetaPageData
+GinNullCategory
+GinOptions
+GinPageOpaque
+GinPageOpaqueData
+GinPlaceToPageRC
+GinPostingList
+GinQualCounts
+GinScanEntry
+GinScanKey
+GinScanOpaque
+GinScanOpaqueData
+GinState
+GinStatsData
+GinTernaryValue
+GinTupleCollector
+GinVacuumState
+GistBuildMode
+GistEntryVector
+GistHstoreOptions
+GistInetKey
+GistNSN
+GistOptBufferingMode
+GistSortedBuildLevelState
+GistSplitUnion
+GistSplitVector
+GistTsVectorOptions
+GistVacState
+GlobalTransaction
+GlobalVisHorizonKind
+GlobalVisState
+GrantRoleStmt
+GrantStmt
+GrantTargetType
+Group
+GroupClause
+GroupPath
+GroupPathExtraData
+GroupResultPath
+GroupState
+GroupVarInfo
+GroupingFunc
+GroupingSet
+GroupingSetData
+GroupingSetKind
+GroupingSetsPath
+GucAction
+GucBoolAssignHook
+GucBoolCheckHook
+GucContext
+GucEnumAssignHook
+GucEnumCheckHook
+GucIntAssignHook
+GucIntCheckHook
+GucRealAssignHook
+GucRealCheckHook
+GucShowHook
+GucSource
+GucStack
+GucStackState
+GucStringAssignHook
+GucStringCheckHook
+HANDLE
+HASHACTION
+HASHBUCKET
+HASHCTL
+HASHELEMENT
+HASHHDR
+HASHSEGMENT
+HASH_SEQ_STATUS
+HE
+HEntry
+HIST_ENTRY
+HKEY
+HLOCAL
+HMAC_CTX
+HMODULE
+HOldEntry
+HRESULT
+HSParser
+HSpool
+HStore
+HTAB
+HTSV_Result
+HV
+Hash
+HashAggBatch
+HashAggSpill
+HashAllocFunc
+HashBuildState
+HashCompareFunc
+HashCopyFunc
+HashIndexStat
+HashInstrumentation
+HashJoin
+HashJoinState
+HashJoinTable
+HashJoinTuple
+HashMemoryChunk
+HashMetaPage
+HashMetaPageData
+HashOptions
+HashPageOpaque
+HashPageOpaqueData
+HashPageStat
+HashPath
+HashScanOpaque
+HashScanOpaqueData
+HashScanPosData
+HashScanPosItem
+HashSkewBucket
+HashState
+HashValueFunc
+HbaLine
+HeadlineJsonState
+HeadlineParsedText
+HeadlineWordEntry
+HeapCheckContext
+HeapScanDesc
+HeapTuple
+HeapTupleData
+HeapTupleFields
+HeapTupleForceOption
+HeapTupleHeader
+HeapTupleHeaderData
+HeapTupleTableSlot
+HistControl
+HotStandbyState
+I32
+ICU_Convert_Func
+ID
+INFIX
+INT128
+INTERFACE_INFO
+IOFuncSelector
+IPCompareMethod
+ITEM
+IV
+IdentLine
+IdentifierLookup
+IdentifySystemCmd
+IfStackElem
+ImportForeignSchemaStmt
+ImportForeignSchemaType
+ImportForeignSchema_function
+ImportQual
+InProgressEnt
+IncludeWal
+InclusionOpaque
+IncrementVarSublevelsUp_context
+IncrementalSort
+IncrementalSortExecutionStatus
+IncrementalSortGroupInfo
+IncrementalSortInfo
+IncrementalSortPath
+IncrementalSortState
+Index
+IndexAMProperty
+IndexAmRoutine
+IndexArrayKeyInfo
+IndexAttachInfo
+IndexAttrBitmapKind
+IndexBuildCallback
+IndexBuildResult
+IndexBulkDeleteCallback
+IndexBulkDeleteResult
+IndexClause
+IndexClauseSet
+IndexDeleteCounts
+IndexDeletePrefetchState
+IndexElem
+IndexFetchHeapData
+IndexFetchTableData
+IndexInfo
+IndexList
+IndexOnlyScan
+IndexOnlyScanState
+IndexOptInfo
+IndexOrderByDistance
+IndexPath
+IndexRuntimeKeyInfo
+IndexScan
+IndexScanDesc
+IndexScanState
+IndexStateFlagsAction
+IndexStmt
+IndexTuple
+IndexTupleData
+IndexUniqueCheck
+IndexVacuumInfo
+IndxInfo
+InferClause
+InferenceElem
+InfoItem
+InhInfo
+InheritableSocket
+InitSampleScan_function
+InitializeDSMForeignScan_function
+InitializeWorkerForeignScan_function
+InlineCodeBlock
+InsertStmt
+Instrumentation
+Int128AggState
+Int8TransTypeData
+IntRBTreeNode
+Integer
+IntegerSet
+InternalDefaultACL
+InternalGrant
+Interval
+IntoClause
+InvalMessageArray
+InvalidationMsgsGroup
+IpcMemoryId
+IpcMemoryKey
+IpcMemoryState
+IpcSemaphoreId
+IpcSemaphoreKey
+IsForeignPathAsyncCapable_function
+IsForeignRelUpdatable_function
+IsForeignScanParallelSafe_function
+IsoConnInfo
+IspellDict
+Item
+ItemId
+ItemIdData
+ItemPointer
+ItemPointerData
+IterateDirectModify_function
+IterateForeignScan_function
+IterateJsonStringValuesState
+JEntry
+JHashState
+JOBOBJECTINFOCLASS
+JOBOBJECT_BASIC_LIMIT_INFORMATION
+JOBOBJECT_BASIC_UI_RESTRICTIONS
+JOBOBJECT_SECURITY_LIMIT_INFORMATION
+JitContext
+JitInstrumentation
+JitProviderCallbacks
+JitProviderCompileExprCB
+JitProviderInit
+JitProviderReleaseContextCB
+JitProviderResetAfterErrorCB
+Join
+JoinCostWorkspace
+JoinExpr
+JoinHashEntry
+JoinPath
+JoinPathExtraData
+JoinState
+JoinType
+JsObject
+JsValue
+JsonAggConstructor
+JsonAggState
+JsonArgument
+JsonArrayAgg
+JsonArrayConstructor
+JsonArrayQueryConstructor
+JsonBaseObjectInfo
+JsonBehavior
+JsonBehaviorType
+JsonCoercion
+JsonCommon
+JsonConstructorExpr
+JsonConstructorType
+JsonEncoding
+JsonExpr
+JsonExprOp
+JsonFormat
+JsonFormatType
+JsonFunc
+JsonFuncExpr
+JsonHashEntry
+JsonIsPredicate
+JsonItemCoercions
+JsonIterateStringValuesAction
+JsonKeyValue
+JsonLexContext
+JsonLikeRegexContext
+JsonManifestFileField
+JsonManifestParseContext
+JsonManifestParseState
+JsonManifestSemanticState
+JsonManifestWALRangeField
+JsonObjectAgg
+JsonObjectConstructor
+JsonOutput
+JsonParseContext
+JsonParseErrorType
+JsonParseExpr
+JsonPath
+JsonPathBool
+JsonPathDatatypeStatus
+JsonPathExecContext
+JsonPathExecResult
+JsonPathGinAddPathItemFunc
+JsonPathGinContext
+JsonPathGinExtractNodesFunc
+JsonPathGinNode
+JsonPathGinNodeType
+JsonPathGinPath
+JsonPathGinPathItem
+JsonPathItem
+JsonPathItemType
+JsonPathKeyword
+JsonPathMutableContext
+JsonPathParseItem
+JsonPathParseResult
+JsonPathPredicateCallback
+JsonPathString
+JsonPathVarCallback
+JsonPathVariableEvalContext
+JsonQuotes
+JsonReturning
+JsonScalarExpr
+JsonSemAction
+JsonTokenType
+JsonTransformStringValuesAction
+JsonTypeCategory
+JsonUniqueBuilderState
+JsonUniqueCheckState
+JsonUniqueHashEntry
+JsonUniqueParsingState
+JsonUniqueStackEntry
+JsonValueExpr
+JsonValueList
+JsonValueListIterator
+JsonValueType
+JsonWrapper
+Jsonb
+JsonbAggState
+JsonbContainer
+JsonbInState
+JsonbIterState
+JsonbIterator
+JsonbIteratorToken
+JsonbPair
+JsonbParseState
+JsonbSubWorkspace
+JsonbTypeCategory
+JsonbValue
+JumbleState
+JunkFilter
+KeyAction
+KeyActions
+KeyArray
+KeySuffix
+KeyWord
+LARGE_INTEGER
+LDAP
+LDAPMessage
+LDAPURLDesc
+LDAP_TIMEVAL
+LINE
+LLVMAttributeRef
+LLVMBasicBlockRef
+LLVMBuilderRef
+LLVMIntPredicate
+LLVMJitContext
+LLVMJitHandle
+LLVMMemoryBufferRef
+LLVMModuleRef
+LLVMOrcJITStackRef
+LLVMOrcModuleHandle
+LLVMOrcTargetAddress
+LLVMPassManagerBuilderRef
+LLVMPassManagerRef
+LLVMSharedModuleRef
+LLVMTargetMachineRef
+LLVMTargetRef
+LLVMTypeRef
+LLVMValueRef
+LOCALLOCK
+LOCALLOCKOWNER
+LOCALLOCKTAG
+LOCALPREDICATELOCK
+LOCK
+LOCKMASK
+LOCKMETHODID
+LOCKMODE
+LOCKTAG
+LONG
+LONG_PTR
+LOOP
+LPBYTE
+LPCTSTR
+LPCWSTR
+LPDWORD
+LPFILETIME
+LPSECURITY_ATTRIBUTES
+LPSERVICE_STATUS
+LPSTR
+LPTHREAD_START_ROUTINE
+LPTSTR
+LPVOID
+LPWSTR
+LSEG
+LUID
+LVPagePruneState
+LVRelState
+LVSavedErrInfo
+LWLock
+LWLockHandle
+LWLockMode
+LWLockPadded
+LZ4F_compressionContext_t
+LZ4F_decompressOptions_t
+LZ4F_decompressionContext_t
+LZ4F_errorCode_t
+LZ4F_preferences_t
+LabelProvider
+LagTracker
+LargeObjectDesc
+LastAttnumInfo
+Latch
+LerpFunc
+LexDescr
+LexemeEntry
+LexemeHashKey
+LexemeInfo
+LexemeKey
+LexizeData
+LibraryInfo
+Limit
+LimitOption
+LimitPath
+LimitState
+LimitStateCond
+List
+ListCell
+ListDictionary
+ListParsedLex
+ListenAction
+ListenActionKind
+ListenStmt
+LoadStmt
+LocalBufferLookupEnt
+LocalPgBackendStatus
+LocalTransactionId
+LocationIndex
+LocationLen
+LockAcquireResult
+LockClauseStrength
+LockData
+LockInfoData
+LockInstanceData
+LockMethod
+LockMethodData
+LockRelId
+LockRows
+LockRowsPath
+LockRowsState
+LockStmt
+LockTagType
+LockTupleMode
+LockViewRecurse_context
+LockWaitPolicy
+LockingClause
+LogOpts
+LogStmtLevel
+LogicalDecodeBeginCB
+LogicalDecodeBeginPrepareCB
+LogicalDecodeChangeCB
+LogicalDecodeCommitCB
+LogicalDecodeCommitPreparedCB
+LogicalDecodeFilterByOriginCB
+LogicalDecodeFilterPrepareCB
+LogicalDecodeMessageCB
+LogicalDecodePrepareCB
+LogicalDecodeRollbackPreparedCB
+LogicalDecodeShutdownCB
+LogicalDecodeStartupCB
+LogicalDecodeStreamAbortCB
+LogicalDecodeStreamChangeCB
+LogicalDecodeStreamCommitCB
+LogicalDecodeStreamMessageCB
+LogicalDecodeStreamPrepareCB
+LogicalDecodeStreamStartCB
+LogicalDecodeStreamStopCB
+LogicalDecodeStreamTruncateCB
+LogicalDecodeTruncateCB
+LogicalDecodingContext
+LogicalErrorCallbackState
+LogicalOutputPluginInit
+LogicalOutputPluginWriterPrepareWrite
+LogicalOutputPluginWriterUpdateProgress
+LogicalOutputPluginWriterWrite
+LogicalRepBeginData
+LogicalRepCommitData
+LogicalRepCommitPreparedTxnData
+LogicalRepCtxStruct
+LogicalRepMsgType
+LogicalRepPartMapEntry
+LogicalRepPreparedTxnData
+LogicalRepRelId
+LogicalRepRelMapEntry
+LogicalRepRelation
+LogicalRepRollbackPreparedTxnData
+LogicalRepTupleData
+LogicalRepTyp
+LogicalRepWorker
+LogicalRewriteMappingData
+LogicalTape
+LogicalTapeSet
+LsnReadQueue
+LsnReadQueueNextFun
+LsnReadQueueNextStatus
+LtreeGistOptions
+LtreeSignature
+MAGIC
+MBuf
+MCVItem
+MCVList
+MEMORY_BASIC_INFORMATION
+MGVTBL
+MINIDUMPWRITEDUMP
+MINIDUMP_TYPE
+MJEvalResult
+MTTargetRelLookup
+MVDependencies
+MVDependency
+MVNDistinct
+MVNDistinctItem
+Material
+MaterialPath
+MaterialState
+MdfdVec
+Memoize
+MemoizeEntry
+MemoizeInstrumentation
+MemoizeKey
+MemoizePath
+MemoizeState
+MemoizeTuple
+MemoryContext
+MemoryContextCallback
+MemoryContextCallbackFunction
+MemoryContextCounters
+MemoryContextData
+MemoryContextMethods
+MemoryStatsPrintFunc
+MergeAction
+MergeActionState
+MergeAppend
+MergeAppendPath
+MergeAppendState
+MergeJoin
+MergeJoinClause
+MergeJoinState
+MergePath
+MergeScanSelCache
+MergeStmt
+MergeWhenClause
+MetaCommand
+MinMaxAggInfo
+MinMaxAggPath
+MinMaxExpr
+MinMaxMultiOptions
+MinMaxOp
+MinimalTuple
+MinimalTupleData
+MinimalTupleTableSlot
+MinmaxMultiOpaque
+MinmaxOpaque
+ModifyTable
+ModifyTableContext
+ModifyTablePath
+ModifyTableState
+MonotonicFunction
+MorphOpaque
+MsgType
+MultiAssignRef
+MultiSortSupport
+MultiSortSupportData
+MultiXactId
+MultiXactMember
+MultiXactOffset
+MultiXactStateData
+MultiXactStatus
+MultirangeIOData
+MultirangeParseState
+MultirangeType
+NDBOX
+NODE
+NTSTATUS
+NUMCacheEntry
+NUMDesc
+NUMProc
+NV
+Name
+NameData
+NameHashEntry
+NamedArgExpr
+NamedLWLockTranche
+NamedLWLockTrancheRequest
+NamedTuplestoreScan
+NamedTuplestoreScanState
+NamespaceInfo
+NestLoop
+NestLoopParam
+NestLoopState
+NestPath
+NewColumnValue
+NewConstraint
+NextSampleBlock_function
+NextSampleTuple_function
+NextValueExpr
+Node
+NodeTag
+NonEmptyRange
+Notification
+NotificationHash
+NotificationList
+NotifyStmt
+Nsrt
+NtDllRoutine
+NullIfExpr
+NullTest
+NullTestType
+NullableDatum
+Numeric
+NumericAggState
+NumericDigit
+NumericSortSupport
+NumericSumAccum
+NumericVar
+OM_uint32
+OP
+OSAPerGroupState
+OSAPerQueryState
+OSInfo
+OSSLCipher
+OSSLDigest
+OVERLAPPED
+ObjectAccessDrop
+ObjectAccessNamespaceSearch
+ObjectAccessPostAlter
+ObjectAccessPostCreate
+ObjectAccessType
+ObjectAddress
+ObjectAddressAndFlags
+ObjectAddressExtra
+ObjectAddressStack
+ObjectAddresses
+ObjectClass
+ObjectPropertyType
+ObjectType
+ObjectWithArgs
+Offset
+OffsetNumber
+OffsetVarNodes_context
+Oid
+OidOptions
+OkeysState
+OldSnapshotControlData
+OldSnapshotTimeMapping
+OldToNewMapping
+OldToNewMappingData
+OnCommitAction
+OnCommitItem
+OnConflictAction
+OnConflictClause
+OnConflictExpr
+OnConflictSetState
+OpBtreeInterpretation
+OpClassCacheEnt
+OpExpr
+OpFamilyMember
+OpFamilyOpFuncGroup
+OpclassInfo
+Operator
+OperatorElement
+OpfamilyInfo
+OprCacheEntry
+OprCacheKey
+OprInfo
+OprProofCacheEntry
+OprProofCacheKey
+OutputContext
+OutputPluginCallbacks
+OutputPluginOptions
+OutputPluginOutputType
+OverrideSearchPath
+OverrideStackEntry
+OverridingKind
+PACE_HEADER
+PACL
+PATH
+PBOOL
+PCtxtHandle
+PERL_CONTEXT
+PERL_SI
+PFN
+PGAlignedBlock
+PGAlignedXLogBlock
+PGAsyncStatusType
+PGCALL2
+PGChecksummablePage
+PGContextVisibility
+PGEvent
+PGEventConnDestroy
+PGEventConnReset
+PGEventId
+PGEventProc
+PGEventRegister
+PGEventResultCopy
+PGEventResultCreate
+PGEventResultDestroy
+PGFInfoFunction
+PGFileType
+PGFunction
+PGLZ_HistEntry
+PGLZ_Strategy
+PGMessageField
+PGModuleMagicFunction
+PGNoticeHooks
+PGOutputData
+PGOutputTxnData
+PGPROC
+PGP_CFB
+PGP_Context
+PGP_MPI
+PGP_PubKey
+PGP_S2K
+PGPing
+PGQueryClass
+PGRUsage
+PGSemaphore
+PGSemaphoreData
+PGShmemHeader
+PGTargetServerType
+PGTernaryBool
+PGTransactionStatusType
+PGVerbosity
+PG_Locale_Strategy
+PG_Lock_Status
+PG_init_t
+PGcancel
+PGcmdQueueEntry
+PGconn
+PGdataValue
+PGlobjfuncs
+PGnotify
+PGpipelineStatus
+PGresAttDesc
+PGresAttValue
+PGresParamDesc
+PGresult
+PGresult_data
+PHANDLE
+PLAINTREE
+PLAssignStmt
+PLUID_AND_ATTRIBUTES
+PLcword
+PLpgSQL_case_when
+PLpgSQL_condition
+PLpgSQL_datum
+PLpgSQL_datum_type
+PLpgSQL_diag_item
+PLpgSQL_exception
+PLpgSQL_exception_block
+PLpgSQL_execstate
+PLpgSQL_expr
+PLpgSQL_func_hashkey
+PLpgSQL_function
+PLpgSQL_getdiag_kind
+PLpgSQL_if_elsif
+PLpgSQL_label_type
+PLpgSQL_nsitem
+PLpgSQL_nsitem_type
+PLpgSQL_plugin
+PLpgSQL_promise_type
+PLpgSQL_raise_option
+PLpgSQL_raise_option_type
+PLpgSQL_rec
+PLpgSQL_recfield
+PLpgSQL_resolve_option
+PLpgSQL_row
+PLpgSQL_stmt
+PLpgSQL_stmt_assert
+PLpgSQL_stmt_assign
+PLpgSQL_stmt_block
+PLpgSQL_stmt_call
+PLpgSQL_stmt_case
+PLpgSQL_stmt_close
+PLpgSQL_stmt_commit
+PLpgSQL_stmt_dynexecute
+PLpgSQL_stmt_dynfors
+PLpgSQL_stmt_execsql
+PLpgSQL_stmt_exit
+PLpgSQL_stmt_fetch
+PLpgSQL_stmt_forc
+PLpgSQL_stmt_foreach_a
+PLpgSQL_stmt_fori
+PLpgSQL_stmt_forq
+PLpgSQL_stmt_fors
+PLpgSQL_stmt_getdiag
+PLpgSQL_stmt_if
+PLpgSQL_stmt_loop
+PLpgSQL_stmt_open
+PLpgSQL_stmt_perform
+PLpgSQL_stmt_raise
+PLpgSQL_stmt_return
+PLpgSQL_stmt_return_next
+PLpgSQL_stmt_return_query
+PLpgSQL_stmt_rollback
+PLpgSQL_stmt_type
+PLpgSQL_stmt_while
+PLpgSQL_trigtype
+PLpgSQL_type
+PLpgSQL_type_type
+PLpgSQL_var
+PLpgSQL_variable
+PLwdatum
+PLword
+PLyArrayToOb
+PLyCursorObject
+PLyDatumToOb
+PLyDatumToObFunc
+PLyExceptionEntry
+PLyExecutionContext
+PLyObToArray
+PLyObToDatum
+PLyObToDatumFunc
+PLyObToDomain
+PLyObToScalar
+PLyObToTransform
+PLyObToTuple
+PLyObject_AsString_t
+PLyPlanObject
+PLyProcedure
+PLyProcedureEntry
+PLyProcedureKey
+PLyResultObject
+PLySRFState
+PLySavedArgs
+PLyScalarToOb
+PLySubtransactionData
+PLySubtransactionObject
+PLyTransformToOb
+PLyTupleToOb
+PLyUnicode_FromStringAndSize_t
+PLy_elog_impl_t
+PMINIDUMP_CALLBACK_INFORMATION
+PMINIDUMP_EXCEPTION_INFORMATION
+PMINIDUMP_USER_STREAM_INFORMATION
+PMSignalData
+PMSignalReason
+PMState
+POLYGON
+PQArgBlock
+PQEnvironmentOption
+PQExpBuffer
+PQExpBufferData
+PQcommMethods
+PQconninfoOption
+PQnoticeProcessor
+PQnoticeReceiver
+PQprintOpt
+PQsslKeyPassHook_OpenSSL_type
+PREDICATELOCK
+PREDICATELOCKTAG
+PREDICATELOCKTARGET
+PREDICATELOCKTARGETTAG
+PROCESS_INFORMATION
+PROCLOCK
+PROCLOCKTAG
+PROC_HDR
+PROC_QUEUE
+PSID
+PSID_AND_ATTRIBUTES
+PSQL_COMP_CASE
+PSQL_ECHO
+PSQL_ECHO_HIDDEN
+PSQL_ERROR_ROLLBACK
+PTEntryArray
+PTIterationArray
+PTOKEN_PRIVILEGES
+PTOKEN_USER
+PULONG
+PUTENVPROC
+PVIndStats
+PVIndVacStatus
+PVOID
+PVShared
+PX_Alias
+PX_Cipher
+PX_Combo
+PX_HMAC
+PX_MD
+Page
+PageData
+PageGistNSN
+PageHeader
+PageHeaderData
+PageXLogRecPtr
+PagetableEntry
+Pairs
+ParallelAppendState
+ParallelBitmapHeapState
+ParallelBlockTableScanDesc
+ParallelBlockTableScanWorker
+ParallelBlockTableScanWorkerData
+ParallelCompletionPtr
+ParallelContext
+ParallelExecutorInfo
+ParallelHashGrowth
+ParallelHashJoinBatch
+ParallelHashJoinBatchAccessor
+ParallelHashJoinState
+ParallelIndexScanDesc
+ParallelReadyList
+ParallelSlot
+ParallelSlotArray
+ParallelSlotResultHandler
+ParallelState
+ParallelTableScanDesc
+ParallelTableScanDescData
+ParallelVacuumState
+ParallelWorkerContext
+ParallelWorkerInfo
+Param
+ParamCompileHook
+ParamExecData
+ParamExternData
+ParamFetchHook
+ParamKind
+ParamListInfo
+ParamPathInfo
+ParamRef
+ParamsErrorCbData
+ParentMapEntry
+ParseCallbackState
+ParseExprKind
+ParseNamespaceColumn
+ParseNamespaceItem
+ParseParamRefHook
+ParseState
+ParsedLex
+ParsedScript
+ParsedText
+ParsedWord
+ParserSetupHook
+ParserState
+PartClauseInfo
+PartClauseMatchStatus
+PartClauseTarget
+PartitionBoundInfo
+PartitionBoundInfoData
+PartitionBoundSpec
+PartitionCmd
+PartitionDesc
+PartitionDescData
+PartitionDirectory
+PartitionDirectoryEntry
+PartitionDispatch
+PartitionElem
+PartitionHashBound
+PartitionKey
+PartitionListValue
+PartitionMap
+PartitionPruneCombineOp
+PartitionPruneContext
+PartitionPruneInfo
+PartitionPruneState
+PartitionPruneStep
+PartitionPruneStepCombine
+PartitionPruneStepOp
+PartitionPruningData
+PartitionRangeBound
+PartitionRangeDatum
+PartitionRangeDatumKind
+PartitionScheme
+PartitionSpec
+PartitionTupleRouting
+PartitionedRelPruneInfo
+PartitionedRelPruningData
+PartitionwiseAggregateType
+PasswordType
+Path
+PathClauseUsage
+PathCostComparison
+PathHashStack
+PathKey
+PathKeyInfo
+PathKeysComparison
+PathTarget
+PathkeyMutatorState
+PathkeySortCost
+PatternInfo
+PatternInfoArray
+Pattern_Prefix_Status
+Pattern_Type
+PendingFsyncEntry
+PendingRelDelete
+PendingRelSync
+PendingUnlinkEntry
+PendingWriteback
+PerLockTagEntry
+PerlInterpreter
+Perl_ppaddr_t
+Permutation
+PermutationStep
+PermutationStepBlocker
+PermutationStepBlockerType
+PgArchData
+PgBackendGSSStatus
+PgBackendSSLStatus
+PgBackendStatus
+PgBenchExpr
+PgBenchExprLink
+PgBenchExprList
+PgBenchExprType
+PgBenchFunction
+PgBenchValue
+PgBenchValueType
+PgChecksumMode
+PgFdwAnalyzeState
+PgFdwConnState
+PgFdwDirectModifyState
+PgFdwModifyState
+PgFdwOption
+PgFdwPathExtraData
+PgFdwRelationInfo
+PgFdwScanState
+PgIfAddrCallback
+PgStatShared_Archiver
+PgStatShared_BgWriter
+PgStatShared_Checkpointer
+PgStatShared_Common
+PgStatShared_Database
+PgStatShared_Function
+PgStatShared_HashEntry
+PgStatShared_Relation
+PgStatShared_ReplSlot
+PgStatShared_SLRU
+PgStatShared_Subscription
+PgStatShared_Wal
+PgStat_ArchiverStats
+PgStat_BackendFunctionEntry
+PgStat_BackendSubEntry
+PgStat_BgWriterStats
+PgStat_CheckpointerStats
+PgStat_Counter
+PgStat_EntryRef
+PgStat_EntryRefHashEntry
+PgStat_FetchConsistency
+PgStat_FunctionCallUsage
+PgStat_FunctionCounts
+PgStat_HashKey
+PgStat_Kind
+PgStat_KindInfo
+PgStat_LocalState
+PgStat_PendingDroppedStatsItem
+PgStat_SLRUStats
+PgStat_ShmemControl
+PgStat_Snapshot
+PgStat_SnapshotEntry
+PgStat_StatDBEntry
+PgStat_StatFuncEntry
+PgStat_StatReplSlotEntry
+PgStat_StatSubEntry
+PgStat_StatTabEntry
+PgStat_SubXactStatus
+PgStat_TableCounts
+PgStat_TableStatus
+PgStat_TableXactStatus
+PgStat_WalStats
+PgXmlErrorContext
+PgXmlStrictness
+Pg_finfo_record
+Pg_magic_struct
+PipeProtoChunk
+PipeProtoHeader
+PlaceHolderInfo
+PlaceHolderVar
+Plan
+PlanDirectModify_function
+PlanForeignModify_function
+PlanInvalItem
+PlanRowMark
+PlanState
+PlannedStmt
+PlannerGlobal
+PlannerInfo
+PlannerParamItem
+Point
+Pointer
+PolicyInfo
+PolyNumAggState
+Pool
+PopulateArrayContext
+PopulateArrayState
+PopulateRecordCache
+PopulateRecordsetState
+Port
+Portal
+PortalHashEnt
+PortalStatus
+PortalStrategy
+PostParseColumnRefHook
+PostgresPollingStatusType
+PostingItem
+PostponedQual
+PreParseColumnRefHook
+PredClass
+PredIterInfo
+PredIterInfoData
+PredXactList
+PredXactListElement
+PredicateLockData
+PredicateLockTargetType
+PrefetchBufferResult
+PrepParallelRestorePtrType
+PrepareStmt
+PreparedStatement
+PresortedKeyData
+PrewarmType
+PrintExtraTocPtrType
+PrintTocDataPtrType
+PrintfArgType
+PrintfArgValue
+PrintfTarget
+PrinttupAttrInfo
+PrivTarget
+PrivateRefCountEntry
+ProcArrayStruct
+ProcLangInfo
+ProcSignalBarrierType
+ProcSignalHeader
+ProcSignalReason
+ProcSignalSlot
+ProcState
+ProcWaitStatus
+ProcessUtilityContext
+ProcessUtility_hook_type
+ProcessingMode
+ProgressCommandType
+ProjectSet
+ProjectSetPath
+ProjectSetState
+ProjectionInfo
+ProjectionPath
+PromptInterruptContext
+ProtocolVersion
+PrsStorage
+PruneState
+PruneStepResult
+PsqlScanCallbacks
+PsqlScanQuoteType
+PsqlScanResult
+PsqlScanState
+PsqlScanStateData
+PsqlSettings
+Publication
+PublicationActions
+PublicationDesc
+PublicationInfo
+PublicationObjSpec
+PublicationObjSpecType
+PublicationPartOpt
+PublicationRelInfo
+PublicationSchemaInfo
+PublicationTable
+PullFilter
+PullFilterOps
+PushFilter
+PushFilterOps
+PushFunction
+PyCFunction
+PyMappingMethods
+PyMethodDef
+PyModuleDef
+PyObject
+PySequenceMethods
+PyTypeObject
+Py_ssize_t
+QPRS_STATE
+QTN2QTState
+QTNode
+QUERYTYPE
+QUERY_SECURITY_CONTEXT_TOKEN_FN
+QualCost
+QualItem
+Query
+QueryCompletion
+QueryDesc
+QueryEnvironment
+QueryInfo
+QueryItem
+QueryItemType
+QueryMode
+QueryOperand
+QueryOperator
+QueryRepresentation
+QueryRepresentationOperand
+QuerySource
+QueueBackendStatus
+QueuePosition
+QuitSignalReason
+RBTNode
+RBTOrderControl
+RBTree
+RBTreeIterator
+REPARSE_JUNCTION_DATA_BUFFER
+RIX
+RI_CompareHashEntry
+RI_CompareKey
+RI_ConstraintInfo
+RI_QueryHashEntry
+RI_QueryKey
+RTEKind
+RWConflict
+RWConflictPoolHeader
+Range
+RangeBound
+RangeBox
+RangeFunction
+RangeIOData
+RangeQueryClause
+RangeSubselect
+RangeTableFunc
+RangeTableFuncCol
+RangeTableSample
+RangeTblEntry
+RangeTblFunction
+RangeTblRef
+RangeType
+RangeVar
+RangeVarGetRelidCallback
+Ranges
+RawColumnDefault
+RawParseMode
+RawStmt
+ReInitializeDSMForeignScan_function
+ReScanForeignScan_function
+ReadBufPtrType
+ReadBufferMode
+ReadBytePtrType
+ReadExtraTocPtrType
+ReadFunc
+ReadLocalXLogPageNoWaitPrivate
+ReadReplicationSlotCmd
+ReassignOwnedStmt
+RecheckForeignScan_function
+RecordCacheEntry
+RecordCompareData
+RecordIOData
+RecoveryLockListsEntry
+RecoveryPauseState
+RecoveryState
+RecoveryTargetTimeLineGoal
+RecoveryTargetType
+RectBox
+RecursionContext
+RecursiveUnion
+RecursiveUnionPath
+RecursiveUnionState
+RefetchForeignRow_function
+RefreshMatViewStmt
+RegProcedure
+Regis
+RegisNode
+RegisteredBgWorker
+ReindexErrorInfo
+ReindexIndexInfo
+ReindexObjectType
+ReindexParams
+ReindexStmt
+ReindexType
+RelFileNode
+RelFileNodeBackend
+RelIdCacheEnt
+RelInfo
+RelInfoArr
+RelMapFile
+RelMapping
+RelOptInfo
+RelOptKind
+RelToCheck
+RelToCluster
+RelabelType
+Relation
+RelationData
+RelationInfo
+RelationPtr
+RelationSyncEntry
+RelcacheCallbackFunction
+ReleaseMatchCB
+RelfilenodeMapEntry
+RelfilenodeMapKey
+Relids
+RelocationBufferInfo
+RelptrFreePageBtree
+RelptrFreePageManager
+RelptrFreePageSpanLeader
+RenameStmt
+ReopenPtrType
+ReorderBuffer
+ReorderBufferApplyChangeCB
+ReorderBufferApplyTruncateCB
+ReorderBufferBeginCB
+ReorderBufferChange
+ReorderBufferChangeType
+ReorderBufferCommitCB
+ReorderBufferCommitPreparedCB
+ReorderBufferDiskChange
+ReorderBufferIterTXNEntry
+ReorderBufferIterTXNState
+ReorderBufferMessageCB
+ReorderBufferPrepareCB
+ReorderBufferRollbackPreparedCB
+ReorderBufferStreamAbortCB
+ReorderBufferStreamChangeCB
+ReorderBufferStreamCommitCB
+ReorderBufferStreamMessageCB
+ReorderBufferStreamPrepareCB
+ReorderBufferStreamStartCB
+ReorderBufferStreamStopCB
+ReorderBufferStreamTruncateCB
+ReorderBufferTXN
+ReorderBufferTXNByIdEnt
+ReorderBufferToastEnt
+ReorderBufferTupleBuf
+ReorderBufferTupleCidEnt
+ReorderBufferTupleCidKey
+ReorderTuple
+RepOriginId
+ReparameterizeForeignPathByChild_function
+ReplaceVarsFromTargetList_context
+ReplaceVarsNoMatchOption
+ReplicaIdentityStmt
+ReplicationKind
+ReplicationSlot
+ReplicationSlotCtlData
+ReplicationSlotOnDisk
+ReplicationSlotPersistency
+ReplicationSlotPersistentData
+ReplicationState
+ReplicationStateCtl
+ReplicationStateOnDisk
+ResTarget
+ReservoirState
+ReservoirStateData
+ResourceArray
+ResourceOwner
+ResourceReleaseCallback
+ResourceReleaseCallbackItem
+ResourceReleasePhase
+RestoreOptions
+RestorePass
+RestrictInfo
+Result
+ResultRelInfo
+ResultState
+ReturnSetInfo
+ReturnStmt
+RevmapContents
+RewriteMappingDataEntry
+RewriteMappingFile
+RewriteRule
+RewriteState
+RmgrData
+RmgrDescData
+RmgrId
+RoleNameItem
+RoleSpec
+RoleSpecType
+RoleStmtType
+RollupData
+RowCompareExpr
+RowCompareType
+RowExpr
+RowIdentityVarInfo
+RowMarkClause
+RowMarkType
+RowSecurityDesc
+RowSecurityPolicy
+RtlGetLastNtStatus_t
+RuleInfo
+RuleLock
+RuleStmt
+RunningTransactions
+RunningTransactionsData
+SC_HANDLE
+SECURITY_ATTRIBUTES
+SECURITY_STATUS
+SEG
+SERIALIZABLEXACT
+SERIALIZABLEXID
+SERIALIZABLEXIDTAG
+SERVICE_STATUS
+SERVICE_STATUS_HANDLE
+SERVICE_TABLE_ENTRY
+SHM_QUEUE
+SID_AND_ATTRIBUTES
+SID_IDENTIFIER_AUTHORITY
+SID_NAME_USE
+SISeg
+SIZE_T
+SMgrRelation
+SMgrRelationData
+SMgrSortArray
+SOCKADDR
+SOCKET
+SPELL
+SPICallbackArg
+SPIExecuteOptions
+SPIParseOpenOptions
+SPIPlanPtr
+SPIPrepareOptions
+SPITupleTable
+SPLITCOST
+SPNode
+SPNodeData
+SPPageDesc
+SQLDropObject
+SQLFunctionCache
+SQLFunctionCachePtr
+SQLFunctionParseInfo
+SQLFunctionParseInfoPtr
+SQLValueFunction
+SQLValueFunctionOp
+SSL
+SSLExtensionInfoContext
+SSL_CTX
+STARTUPINFO
+STRLEN
+SV
+SYNCHRONIZATION_BARRIER
+SampleScan
+SampleScanGetSampleSize_function
+SampleScanState
+SavedTransactionCharacteristics
+ScalarArrayOpExpr
+ScalarArrayOpExprHashEntry
+ScalarArrayOpExprHashTable
+ScalarIOData
+ScalarItem
+ScalarMCVItem
+Scan
+ScanDirection
+ScanKey
+ScanKeyData
+ScanKeywordHashFunc
+ScanKeywordList
+ScanState
+ScanTypeControl
+ScannerCallbackState
+SchemaQuery
+SecBuffer
+SecBufferDesc
+SecLabelItem
+SecLabelStmt
+SeenRelsEntry
+SelectLimit
+SelectStmt
+Selectivity
+SemTPadded
+SemiAntiJoinFactors
+SeqScan
+SeqScanState
+SeqTable
+SeqTableData
+SerCommitSeqNo
+SerialControl
+SerializableXactHandle
+SerializedActiveRelMaps
+SerializedRanges
+SerializedReindexState
+SerializedSnapshotData
+SerializedTransactionState
+Session
+SessionBackupState
+SessionEndType
+SetConstraintState
+SetConstraintStateData
+SetConstraintTriggerData
+SetExprState
+SetFunctionReturnMode
+SetOp
+SetOpCmd
+SetOpPath
+SetOpState
+SetOpStatePerGroup
+SetOpStrategy
+SetOperation
+SetOperationStmt
+SetQuantifier
+SetToDefault
+SetupWorkerPtrType
+ShDependObjectInfo
+SharedAggInfo
+SharedBitmapState
+SharedDependencyObjectType
+SharedDependencyType
+SharedExecutorInstrumentation
+SharedFileSet
+SharedHashInfo
+SharedIncrementalSortInfo
+SharedInvalCatalogMsg
+SharedInvalCatcacheMsg
+SharedInvalRelcacheMsg
+SharedInvalRelmapMsg
+SharedInvalSmgrMsg
+SharedInvalSnapshotMsg
+SharedInvalidationMessage
+SharedJitInstrumentation
+SharedMemoizeInfo
+SharedRecordTableEntry
+SharedRecordTableKey
+SharedRecordTypmodRegistry
+SharedSortInfo
+SharedTuplestore
+SharedTuplestoreAccessor
+SharedTuplestoreChunk
+SharedTuplestoreParticipant
+SharedTypmodTableEntry
+Sharedsort
+ShellTypeInfo
+ShippableCacheEntry
+ShippableCacheKey
+ShmemIndexEnt
+ShutdownForeignScan_function
+ShutdownInformation
+ShutdownMode
+SignTSVector
+SimpleActionList
+SimpleActionListCell
+SimpleEcontextStackEntry
+SimpleOidList
+SimpleOidListCell
+SimplePtrList
+SimplePtrListCell
+SimpleStats
+SimpleStringList
+SimpleStringListCell
+SingleBoundSortItem
+Size
+SkipPages
+SlabBlock
+SlabChunk
+SlabContext
+SlabSlot
+SlotNumber
+SlruCtl
+SlruCtlData
+SlruErrorCause
+SlruPageStatus
+SlruScanCallback
+SlruShared
+SlruSharedData
+SlruWriteAll
+SlruWriteAllData
+SnapBuild
+SnapBuildOnDisk
+SnapBuildState
+Snapshot
+SnapshotData
+SnapshotType
+SockAddr
+Sort
+SortBy
+SortByDir
+SortByNulls
+SortCoordinate
+SortGroupClause
+SortItem
+SortPath
+SortShimExtra
+SortState
+SortSupport
+SortSupportData
+SortTuple
+SortTupleComparator
+SortedPoint
+SpGistBuildState
+SpGistCache
+SpGistDeadTuple
+SpGistDeadTupleData
+SpGistInnerTuple
+SpGistInnerTupleData
+SpGistLUPCache
+SpGistLastUsedPage
+SpGistLeafTuple
+SpGistLeafTupleData
+SpGistMetaPageData
+SpGistNodeTuple
+SpGistNodeTupleData
+SpGistOptions
+SpGistPageOpaque
+SpGistPageOpaqueData
+SpGistScanOpaque
+SpGistScanOpaqueData
+SpGistSearchItem
+SpGistState
+SpGistTypeDesc
+SpecialJoinInfo
+SpinDelayStatus
+SplitInterval
+SplitLR
+SplitPoint
+SplitTextOutputData
+SplitVar
+SplitedPageLayout
+StackElem
+StartBlobPtrType
+StartBlobsPtrType
+StartDataPtrType
+StartReplicationCmd
+StartupStatusEnum
+StatEntry
+StatExtEntry
+StateFileChunk
+StatisticExtInfo
+StatsBuildData
+StatsData
+StatsElem
+StatsExtInfo
+StdAnalyzeData
+StdRdOptIndexCleanup
+StdRdOptions
+Step
+StopList
+StrategyNumber
+StreamCtl
+String
+StringInfo
+StringInfoData
+StripnullState
+SubLink
+SubLinkType
+SubOpts
+SubPlan
+SubPlanState
+SubRemoveRels
+SubTransactionId
+SubXactCallback
+SubXactCallbackItem
+SubXactEvent
+SubXactInfo
+SubqueryScan
+SubqueryScanPath
+SubqueryScanState
+SubqueryScanStatus
+SubscriptExecSetup
+SubscriptExecSteps
+SubscriptRoutines
+SubscriptTransform
+SubscriptingRef
+SubscriptingRefState
+Subscription
+SubscriptionInfo
+SubscriptionRelState
+SupportRequestCost
+SupportRequestIndexCondition
+SupportRequestRows
+SupportRequestSelectivity
+SupportRequestSimplify
+SupportRequestWFuncMonotonic
+Syn
+SyncOps
+SyncRepConfigData
+SyncRepStandbyData
+SyncRequestHandler
+SyncRequestType
+SysFKRelationship
+SysScanDesc
+SyscacheCallbackFunction
+SystemRowsSamplerData
+SystemSamplerData
+SystemTimeSamplerData
+TAR_MEMBER
+TBMIterateResult
+TBMIteratingState
+TBMIterator
+TBMSharedIterator
+TBMSharedIteratorState
+TBMStatus
+TBlockState
+TIDBitmap
+TM_FailureData
+TM_IndexDelete
+TM_IndexDeleteOp
+TM_IndexStatus
+TM_Result
+TOKEN_DEFAULT_DACL
+TOKEN_INFORMATION_CLASS
+TOKEN_PRIVILEGES
+TOKEN_USER
+TParser
+TParserCharTest
+TParserPosition
+TParserSpecial
+TParserState
+TParserStateAction
+TParserStateActionItem
+TQueueDestReceiver
+TRGM
+TSAnyCacheEntry
+TSConfigCacheEntry
+TSConfigInfo
+TSDictInfo
+TSDictionaryCacheEntry
+TSExecuteCallback
+TSLexeme
+TSParserCacheEntry
+TSParserInfo
+TSQuery
+TSQueryData
+TSQueryParserState
+TSQuerySign
+TSReadPointer
+TSTemplateInfo
+TSTernaryValue
+TSTokenTypeStorage
+TSVector
+TSVectorBuildState
+TSVectorData
+TSVectorParseState
+TSVectorStat
+TState
+TStatus
+TStoreState
+TXNEntryFile
+TYPCATEGORY
+T_Action
+T_WorkerStatus
+TableAmRoutine
+TableAttachInfo
+TableDataInfo
+TableFunc
+TableFuncRoutine
+TableFuncScan
+TableFuncScanState
+TableInfo
+TableLikeClause
+TableSampleClause
+TableScanDesc
+TableScanDescData
+TableSpaceCacheEntry
+TableSpaceOpts
+TablespaceList
+TablespaceListCell
+TapeBlockTrailer
+TapeShare
+TarMethodData
+TarMethodFile
+TargetEntry
+TclExceptionNameMap
+Tcl_DString
+Tcl_FileProc
+Tcl_HashEntry
+Tcl_HashTable
+Tcl_Interp
+Tcl_NotifierProcs
+Tcl_Obj
+Tcl_Time
+TempNamespaceStatus
+TestDecodingData
+TestDecodingTxnData
+TestSpec
+TextFreq
+TextPositionState
+TheLexeme
+TheSubstitute
+TidExpr
+TidExprType
+TidHashKey
+TidOpExpr
+TidPath
+TidRangePath
+TidRangeScan
+TidRangeScanState
+TidScan
+TidScanState
+TimeADT
+TimeLineHistoryCmd
+TimeLineHistoryEntry
+TimeLineID
+TimeOffset
+TimeStamp
+TimeTzADT
+TimeZoneAbbrevTable
+TimeoutId
+TimeoutType
+Timestamp
+TimestampTz
+TmFromChar
+TmToChar
+ToastAttrInfo
+ToastCompressionId
+ToastTupleContext
+ToastedAttribute
+TocEntry
+TokenAuxData
+TokenizedAuthLine
+TrackItem
+TransInvalidationInfo
+TransState
+TransactionId
+TransactionState
+TransactionStateData
+TransactionStmt
+TransactionStmtKind
+TransformInfo
+TransformJsonStringValuesState
+TransitionCaptureState
+TrgmArc
+TrgmArcInfo
+TrgmBound
+TrgmColor
+TrgmColorInfo
+TrgmGistOptions
+TrgmNFA
+TrgmPackArcInfo
+TrgmPackedArc
+TrgmPackedGraph
+TrgmPackedState
+TrgmPrefix
+TrgmState
+TrgmStateKey
+TrieChar
+Trigger
+TriggerData
+TriggerDesc
+TriggerEvent
+TriggerFlags
+TriggerInfo
+TriggerTransition
+TruncateStmt
+TsmRoutine
+TupOutputState
+TupSortStatus
+TupStoreStatus
+TupleConstr
+TupleConversionMap
+TupleDesc
+TupleHashEntry
+TupleHashEntryData
+TupleHashIterator
+TupleHashTable
+TupleQueueReader
+TupleTableSlot
+TupleTableSlotOps
+TuplesortInstrumentation
+TuplesortMethod
+TuplesortSpaceType
+Tuplesortstate
+Tuplestorestate
+TwoPhaseCallback
+TwoPhaseFileHeader
+TwoPhaseLockRecord
+TwoPhasePgStatRecord
+TwoPhasePredicateLockRecord
+TwoPhasePredicateRecord
+TwoPhasePredicateRecordType
+TwoPhasePredicateXactRecord
+TwoPhaseRecordOnDisk
+TwoPhaseRmgrId
+TwoPhaseStateData
+Type
+TypeCacheEntry
+TypeCacheEnumData
+TypeCast
+TypeCat
+TypeFuncClass
+TypeInfo
+TypeName
+U
+U32
+U8
+UChar
+UCharIterator
+UColAttribute
+UColAttributeValue
+UCollator
+UConverter
+UErrorCode
+UINT
+ULARGE_INTEGER
+ULONG
+ULONG_PTR
+UV
+UVersionInfo
+UnicodeNormalizationForm
+UnicodeNormalizationQC
+Unique
+UniquePath
+UniquePathMethod
+UniqueState
+UnlistenStmt
+UnresolvedTup
+UnresolvedTupData
+UpdateContext
+UpdateStmt
+UpperRelationKind
+UpperUniquePath
+UserAuth
+UserMapping
+UserOpts
+VacAttrStats
+VacAttrStatsP
+VacDeadItems
+VacErrPhase
+VacOptValue
+VacuumParams
+VacuumRelation
+VacuumStmt
+ValidateIndexState
+ValuesScan
+ValuesScanState
+Var
+VarBit
+VarChar
+VarParamState
+VarString
+VarStringSortSupport
+Variable
+VariableAssignHook
+VariableCache
+VariableCacheData
+VariableSetKind
+VariableSetStmt
+VariableShowStmt
+VariableSpace
+VariableStatData
+VariableSubstituteHook
+Variables
+VersionedQuery
+Vfd
+ViewCheckOption
+ViewOptCheckOption
+ViewOptions
+ViewStmt
+VirtualTransactionId
+VirtualTupleTableSlot
+VolatileFunctionStatus
+Vsrt
+WAIT_ORDER
+WALAvailability
+WALInsertLock
+WALInsertLockPadded
+WALOpenSegment
+WALReadError
+WALSegmentCloseCB
+WALSegmentContext
+WALSegmentOpenCB
+WCHAR
+WCOKind
+WFW_WaitOption
+WIDGET
+WORD
+WORKSTATE
+WSABUF
+WSADATA
+WSANETWORKEVENTS
+WSAPROTOCOL_INFO
+WaitEvent
+WaitEventActivity
+WaitEventClient
+WaitEventIO
+WaitEventIPC
+WaitEventSet
+WaitEventTimeout
+WaitPMResult
+WalCloseMethod
+WalCompression
+WalLevel
+WalRcvData
+WalRcvExecResult
+WalRcvExecStatus
+WalRcvState
+WalRcvStreamOptions
+WalReceiverConn
+WalReceiverFunctionsType
+WalSnd
+WalSndCtlData
+WalSndSendDataCallback
+WalSndState
+WalTimeSample
+WalUsage
+WalWriteMethod
+Walfile
+WindowAgg
+WindowAggPath
+WindowAggState
+WindowAggStatus
+WindowClause
+WindowClauseSortData
+WindowDef
+WindowFunc
+WindowFuncExprState
+WindowFuncLists
+WindowObject
+WindowObjectData
+WindowStatePerAgg
+WindowStatePerAggData
+WindowStatePerFunc
+WithCheckOption
+WithClause
+WordEntry
+WordEntryIN
+WordEntryPos
+WordEntryPosVector
+WordEntryPosVector1
+WorkTableScan
+WorkTableScanState
+WorkerInfo
+WorkerInfoData
+WorkerInstrumentation
+WorkerJobDumpPtrType
+WorkerJobRestorePtrType
+Working_State
+WriteBufPtrType
+WriteBytePtrType
+WriteDataCallback
+WriteDataPtrType
+WriteExtraTocPtrType
+WriteFunc
+WriteManifestState
+WriteTarState
+WritebackContext
+X509
+X509_EXTENSION
+X509_NAME
+X509_NAME_ENTRY
+X509_STORE
+X509_STORE_CTX
+XLTW_Oper
+XLogCtlData
+XLogCtlInsert
+XLogDumpConfig
+XLogDumpPrivate
+XLogLongPageHeader
+XLogLongPageHeaderData
+XLogPageHeader
+XLogPageHeaderData
+XLogPageReadCB
+XLogPageReadPrivate
+XLogPageReadResult
+XLogPrefetchStats
+XLogPrefetcher
+XLogPrefetcherFilter
+XLogReaderRoutine
+XLogReaderState
+XLogRecData
+XLogRecPtr
+XLogRecStats
+XLogRecord
+XLogRecordBlockCompressHeader
+XLogRecordBlockHeader
+XLogRecordBlockImageHeader
+XLogRecordBuffer
+XLogRecoveryCtlData
+XLogRedoAction
+XLogSegNo
+XLogSource
+XLogStats
+XLogwrtResult
+XLogwrtRqst
+XPV
+XPVIV
+XPVMG
+XactCallback
+XactCallbackItem
+XactEvent
+XactLockTableWaitInfo
+XidBoundsViolation
+XidCacheStatus
+XidCommitStatus
+XidStatus
+XmlExpr
+XmlExprOp
+XmlOptionType
+XmlSerialize
+XmlTableBuilderData
+YYLTYPE
+YYSTYPE
+YY_BUFFER_STATE
+ZSTD_CCtx
+ZSTD_DCtx
+ZSTD_inBuffer
+ZSTD_outBuffer
+_SPI_connection
+_SPI_plan
+__AssignProcessToJobObject
+__CreateJobObject
+__CreateRestrictedToken
+__IsProcessInJob
+__QueryInformationJobObject
+__SetInformationJobObject
+__time64_t
+_dev_t
+_ino_t
+_locale_t
+_resultmap
+_stringlist
+acquireLocksOnSubLinks_context
+adjust_appendrel_attrs_context
+aff_regex_struct
+allocfunc
+amadjustmembers_function
+ambeginscan_function
+ambuild_function
+ambuildempty_function
+ambuildphasename_function
+ambulkdelete_function
+amcanreturn_function
+amcostestimate_function
+amendscan_function
+amestimateparallelscan_function
+amgetbitmap_function
+amgettuple_function
+aminitparallelscan_function
+aminsert_function
+ammarkpos_function
+amoptions_function
+amparallelrescan_function
+amproperty_function
+amrescan_function
+amrestrpos_function
+amvacuumcleanup_function
+amvalidate_function
+array_iter
+array_unnest_fctx
+assign_collations_context
+autovac_table
+av_relation
+avl_dbase
+avl_node
+avl_tree
+avw_dbase
+backslashResult
+backup_manifest_info
+backup_manifest_option
+base_yy_extra_type
+basebackup_options
+bbsink
+bbsink_copystream
+bbsink_gzip
+bbsink_lz4
+bbsink_ops
+bbsink_server
+bbsink_shell
+bbsink_state
+bbsink_throttle
+bbsink_zstd
+bbstreamer
+bbstreamer_archive_context
+bbstreamer_extractor
+bbstreamer_gzip_decompressor
+bbstreamer_gzip_writer
+bbstreamer_lz4_frame
+bbstreamer_member
+bbstreamer_ops
+bbstreamer_plain_writer
+bbstreamer_recovery_injector
+bbstreamer_tar_archiver
+bbstreamer_tar_parser
+bbstreamer_zstd_frame
+bgworker_main_type
+binaryheap
+binaryheap_comparator
+bitmapword
+bits16
+bits32
+bits8
+bloom_filter
+boolKEY
+brin_column_state
+brin_serialize_callback_type
+bytea
+cached_re_str
+canonicalize_state
+cashKEY
+catalogid_hash
+cfp
+check_agg_arguments_context
+check_function_callback
+check_network_data
+check_object_relabel_type
+check_password_hook_type
+check_ungrouped_columns_context
+chr
+clock_t
+cmpEntriesArg
+codes_t
+collation_cache_entry
+color
+colormaprange
+compare_context
+config_var_value
+contain_aggs_of_level_context
+convert_testexpr_context
+copy_data_source_cb
+core_YYSTYPE
+core_yy_extra_type
+core_yyscan_t
+corrupt_items
+cost_qual_eval_context
+cp_hash_func
+create_upper_paths_hook_type
+createdb_failure_params
+crosstab_HashEnt
+crosstab_cat_desc
+datapagemap_iterator_t
+datapagemap_t
+dateKEY
+datetkn
+dce_uuid_t
+decimal
+deparse_columns
+deparse_context
+deparse_expr_cxt
+deparse_namespace
+destructor
+dev_t
+digit
+disassembledLeaf
+dlist_head
+dlist_iter
+dlist_mutable_iter
+dlist_node
+ds_state
+dsa_area
+dsa_area_control
+dsa_area_pool
+dsa_area_span
+dsa_handle
+dsa_pointer
+dsa_pointer_atomic
+dsa_segment_header
+dsa_segment_index
+dsa_segment_map
+dshash_compare_function
+dshash_hash
+dshash_hash_function
+dshash_parameters
+dshash_partition
+dshash_seq_status
+dshash_table
+dshash_table_control
+dshash_table_handle
+dshash_table_item
+dsm_control_header
+dsm_control_item
+dsm_handle
+dsm_op
+dsm_segment
+dsm_segment_detach_callback
+eLogType
+ean13
+eary
+ec_matches_callback_type
+ec_member_foreign_arg
+ec_member_matches_arg
+emit_log_hook_type
+eval_const_expressions_context
+exec_thread_arg
+execution_state
+explain_get_index_name_hook_type
+f_smgr
+fd_set
+fe_scram_state
+fe_scram_state_enum
+fetch_range_request
+file_action_t
+file_entry_t
+file_type_t
+filehash_hash
+filehash_iterator
+filemap_t
+fill_string_relopt
+finalize_primnode_context
+find_dependent_phvs_context
+find_expr_references_context
+fix_join_expr_context
+fix_scan_expr_context
+fix_upper_expr_context
+fix_windowagg_cond_context
+flatten_join_alias_vars_context
+float4
+float4KEY
+float8
+float8KEY
+floating_decimal_32
+floating_decimal_64
+fmAggrefPtr
+fmExprContextCallbackFunction
+fmNodePtr
+fmStringInfo
+fmgr_hook_type
+foreign_glob_cxt
+foreign_loc_cxt
+freeaddrinfo_ptr_t
+freefunc
+fsec_t
+gbt_vsrt_arg
+gbtree_ninfo
+gbtree_vinfo
+generate_series_fctx
+generate_series_numeric_fctx
+generate_series_timestamp_fctx
+generate_series_timestamptz_fctx
+generate_subscripts_fctx
+get_attavgwidth_hook_type
+get_index_stats_hook_type
+get_relation_info_hook_type
+get_relation_stats_hook_type
+getaddrinfo_ptr_t
+getnameinfo_ptr_t
+gid_t
+gin_leafpage_items_state
+ginxlogCreatePostingTree
+ginxlogDeleteListPages
+ginxlogDeletePage
+ginxlogInsert
+ginxlogInsertDataInternal
+ginxlogInsertEntry
+ginxlogInsertListPage
+ginxlogRecompressDataLeaf
+ginxlogSplit
+ginxlogUpdateMeta
+ginxlogVacuumDataLeafPage
+gistxlogDelete
+gistxlogPage
+gistxlogPageDelete
+gistxlogPageReuse
+gistxlogPageSplit
+gistxlogPageUpdate
+grouping_sets_data
+gseg_picksplit_item
+gss_buffer_desc
+gss_cred_id_t
+gss_ctx_id_t
+gss_name_t
+gtrgm_consistent_cache
+gzFile
+hashfunc
+hbaPort
+heap_page_items_state
+help_handler
+hlCheck
+hstoreCheckKeyLen_t
+hstoreCheckValLen_t
+hstorePairs_t
+hstoreUniquePairs_t
+hstoreUpgrade_t
+hyperLogLogState
+ifState
+ilist
+import_error_callback_arg
+indexed_tlist
+inet
+inetKEY
+inet_struct
+init_function
+inline_cte_walker_context
+inline_error_callback_arg
+ino_t
+instr_time
+int128
+int16
+int16KEY
+int2vector
+int32
+int32KEY
+int32_t
+int64
+int64KEY
+int8
+internalPQconninfoOption
+intptr_t
+intset_internal_node
+intset_leaf_node
+intset_node
+intvKEY
+itemIdCompact
+itemIdCompactData
+iterator
+jmp_buf
+join_search_hook_type
+json_aelem_action
+json_manifest_error_callback
+json_manifest_perfile_callback
+json_manifest_perwalrange_callback
+json_ofield_action
+json_scalar_action
+json_struct_action
+keyEntryData
+key_t
+lclContext
+lclTocEntry
+leafSegmentInfo
+leaf_item
+libpq_source
+line_t
+lineno_t
+list_sort_comparator
+local_relopt
+local_relopts
+local_source
+locale_t
+locate_agg_of_level_context
+locate_var_of_level_context
+locate_windowfunc_context
+logstreamer_param
+lquery
+lquery_level
+lquery_variant
+ltree
+ltree_gist
+ltree_level
+ltxtquery
+mXactCacheEnt
+mac8KEY
+macKEY
+macaddr
+macaddr8
+macaddr_sortsupport_state
+manifest_file
+manifest_files_hash
+manifest_files_iterator
+manifest_wal_range
+map_variable_attnos_context
+max_parallel_hazard_context
+mb2wchar_with_len_converter
+mbchar_verifier
+mbcharacter_incrementer
+mbdisplaylen_converter
+mblen_converter
+mbstr_verifier
+memoize_hash
+memoize_iterator
+metastring
+mix_data_t
+mixedStruct
+mode_t
+movedb_failure_params
+multirange_bsearch_comparison
+multirange_unnest_fctx
+mxact
+mxtruncinfo
+needs_fmgr_hook_type
+network_sortsupport_state
+nodeitem
+normal_rand_fctx
+ntile_context
+numeric
+object_access_hook_type
+object_access_hook_type_str
+off_t
+oidKEY
+oidvector
+on_dsm_detach_callback
+on_exit_nicely_callback
+openssl_tls_init_hook_typ
+ossl_EVP_cipher_func
+other
+output_type
+pagetable_hash
+pagetable_iterator
+pairingheap
+pairingheap_comparator
+pairingheap_node
+parallel_worker_main_type
+parse_error_callback_arg
+parser_context
+partition_method_t
+pendingPosition
+pgParameterStatus
+pg_atomic_flag
+pg_atomic_uint32
+pg_atomic_uint64
+pg_be_sasl_mech
+pg_checksum_context
+pg_checksum_raw_context
+pg_checksum_type
+pg_compress_algorithm
+pg_compress_specification
+pg_conn_host
+pg_conn_host_type
+pg_conv_map
+pg_crc32
+pg_crc32c
+pg_cryptohash_ctx
+pg_cryptohash_errno
+pg_cryptohash_type
+pg_ctype_cache
+pg_enc
+pg_enc2gettext
+pg_enc2name
+pg_encname
+pg_fe_sasl_mech
+pg_funcptr_t
+pg_gssinfo
+pg_hmac_ctx
+pg_hmac_errno
+pg_int64
+pg_local_to_utf_combined
+pg_locale_t
+pg_mb_radix_tree
+pg_md5_ctx
+pg_on_exit_callback
+pg_prng_state
+pg_re_flags
+pg_saslprep_rc
+pg_sha1_ctx
+pg_sha224_ctx
+pg_sha256_ctx
+pg_sha384_ctx
+pg_sha512_ctx
+pg_snapshot
+pg_stack_base_t
+pg_time_t
+pg_time_usec_t
+pg_tz
+pg_tz_cache
+pg_tzenum
+pg_unicode_decompinfo
+pg_unicode_decomposition
+pg_unicode_norminfo
+pg_unicode_normprops
+pg_unicode_recompinfo
+pg_utf_to_local_combined
+pg_uuid_t
+pg_wc_probefunc
+pg_wchar
+pg_wchar_tbl
+pgp_armor_headers_state
+pgpid_t
+pgsocket
+pgsql_thing_t
+pgssEntry
+pgssGlobalStats
+pgssHashKey
+pgssSharedState
+pgssStoreKind
+pgssVersion
+pgstat_entry_ref_hash_hash
+pgstat_entry_ref_hash_iterator
+pgstat_page
+pgstat_snapshot_hash
+pgstattuple_type
+pgthreadlock_t
+pid_t
+pivot_field
+planner_hook_type
+plperl_array_info
+plperl_call_data
+plperl_interp_desc
+plperl_proc_desc
+plperl_proc_key
+plperl_proc_ptr
+plperl_query_desc
+plperl_query_entry
+plpgsql_CastHashEntry
+plpgsql_CastHashKey
+plpgsql_HashEnt
+pltcl_call_state
+pltcl_interp_desc
+pltcl_proc_desc
+pltcl_proc_key
+pltcl_proc_ptr
+pltcl_query_desc
+pointer
+polymorphic_actuals
+pos_trgm
+post_parse_analyze_hook_type
+postprocess_result_function
+pqbool
+pqsigfunc
+printQueryOpt
+printTableContent
+printTableFooter
+printTableOpt
+printTextFormat
+printTextLineFormat
+printTextLineWrap
+printTextRule
+printfunc
+priv_map
+process_file_callback_t
+process_sublinks_context
+proclist_head
+proclist_mutable_iter
+proclist_node
+promptStatus_t
+pthread_barrier_t
+pthread_cond_t
+pthread_key_t
+pthread_mutex_t
+pthread_once_t
+pthread_t
+ptrdiff_t
+pull_var_clause_context
+pull_varattnos_context
+pull_varnos_context
+pull_vars_context
+pullup_replace_vars_context
+pushdown_safety_info
+qc_hash_func
+qsort_arg_comparator
+qsort_comparator
+query_pathkeys_callback
+radius_attribute
+radius_packet
+rangeTableEntry_used_context
+rank_context
+rbt_allocfunc
+rbt_combiner
+rbt_comparator
+rbt_freefunc
+reduce_outer_joins_state
+reference
+regex_arc_t
+regex_t
+regexp
+regexp_matches_ctx
+registered_buffer
+regmatch_t
+regoff_t
+regproc
+relopt_bool
+relopt_enum
+relopt_enum_elt_def
+relopt_gen
+relopt_int
+relopt_kind
+relopt_parse_elt
+relopt_real
+relopt_string
+relopt_type
+relopt_value
+relopts_validator
+remoteConn
+remoteConnHashEnt
+remoteDep
+rendezvousHashEntry
+replace_rte_variables_callback
+replace_rte_variables_context
+ret_type
+rewind_source
+rewrite_event
+rf_context
+rm_detail_t
+role_auth_extra
+row_security_policy_hook_type
+rsv_callback
+saophash_hash
+save_buffer
+scram_state
+scram_state_enum
+sem_t
+sequence_magic
+set_join_pathlist_hook_type
+set_rel_pathlist_hook_type
+shm_mq
+shm_mq_handle
+shm_mq_iovec
+shm_mq_result
+shm_toc
+shm_toc_entry
+shm_toc_estimator
+shmem_request_hook_type
+shmem_startup_hook_type
+sig_atomic_t
+sigjmp_buf
+signedbitmapword
+sigset_t
+size_t
+slist_head
+slist_iter
+slist_mutable_iter
+slist_node
+slock_t
+socket_set
+socklen_t
+spgBulkDeleteState
+spgChooseIn
+spgChooseOut
+spgChooseResultType
+spgConfigIn
+spgConfigOut
+spgInnerConsistentIn
+spgInnerConsistentOut
+spgLeafConsistentIn
+spgLeafConsistentOut
+spgNodePtr
+spgPickSplitIn
+spgPickSplitOut
+spgVacPendingItem
+spgxlogAddLeaf
+spgxlogAddNode
+spgxlogMoveLeafs
+spgxlogPickSplit
+spgxlogSplitTuple
+spgxlogState
+spgxlogVacuumLeaf
+spgxlogVacuumRedirect
+spgxlogVacuumRoot
+split_pathtarget_context
+split_pathtarget_item
+sql_error_callback_arg
+sqlparseInfo
+sqlparseState
+ss_lru_item_t
+ss_scan_location_t
+ss_scan_locations_t
+ssize_t
+standard_qp_extra
+stemmer_module
+stmtCacheEntry
+storeInfo
+storeRes_func
+stream_stop_callback
+string
+substitute_actual_parameters_context
+substitute_actual_srf_parameters_context
+substitute_phv_relids_context
+symbol
+tablespaceinfo
+teSection
+temp_tablespaces_extra
+test_re_flags
+test_regex_ctx
+test_shm_mq_header
+test_spec
+test_start_function
+text
+timeKEY
+time_t
+timeout_handler_proc
+timeout_params
+timerCA
+tlist_vinfo
+toast_compress_header
+transferMode
+transfer_thread_arg
+trgm
+trgm_mb_char
+trivalue
+tsKEY
+ts_parserstate
+ts_tokenizer
+ts_tokentype
+tsearch_readline_state
+tuplehash_hash
+tuplehash_iterator
+type
+tzEntry
+u_char
+u_int
+uchr
+uid_t
+uint128
+uint16
+uint16_t
+uint32
+uint32_t
+uint64
+uint64_t
+uint8
+uint8_t
+uintptr_t
+unicodeStyleBorderFormat
+unicodeStyleColumnFormat
+unicodeStyleFormat
+unicodeStyleRowFormat
+unicode_linestyle
+unit_conversion
+unlogged_relation_entry
+utf_local_conversion_func
+uuidKEY
+uuid_rc_t
+uuid_sortsupport_state
+uuid_t
+va_list
+vacuumingOptions
+validate_string_relopt
+varatt_expanded
+varattrib_1b
+varattrib_1b_e
+varattrib_4b
+vbits
+verifier_context
+walrcv_check_conninfo_fn
+walrcv_connect_fn
+walrcv_create_slot_fn
+walrcv_disconnect_fn
+walrcv_endstreaming_fn
+walrcv_exec_fn
+walrcv_get_backend_pid_fn
+walrcv_get_conninfo_fn
+walrcv_get_senderinfo_fn
+walrcv_identify_system_fn
+walrcv_readtimelinehistoryfile_fn
+walrcv_receive_fn
+walrcv_send_fn
+walrcv_server_version_fn
+walrcv_startstreaming_fn
+wchar2mb_with_len_converter
+wchar_t
+win32_deadchild_waitinfo
+wint_t
+worker_state
+worktable
+wrap
+xl_brin_createidx
+xl_brin_desummarize
+xl_brin_insert
+xl_brin_revmap_extend
+xl_brin_samepage_update
+xl_brin_update
+xl_btree_dedup
+xl_btree_delete
+xl_btree_insert
+xl_btree_mark_page_halfdead
+xl_btree_metadata
+xl_btree_newroot
+xl_btree_reuse_page
+xl_btree_split
+xl_btree_unlink_page
+xl_btree_update
+xl_btree_vacuum
+xl_clog_truncate
+xl_commit_ts_truncate
+xl_dbase_create_file_copy_rec
+xl_dbase_create_wal_log_rec
+xl_dbase_drop_rec
+xl_end_of_recovery
+xl_hash_add_ovfl_page
+xl_hash_delete
+xl_hash_init_bitmap_page
+xl_hash_init_meta_page
+xl_hash_insert
+xl_hash_move_page_contents
+xl_hash_split_allocate_page
+xl_hash_split_complete
+xl_hash_squeeze_page
+xl_hash_update_meta_page
+xl_hash_vacuum_one_page
+xl_heap_confirm
+xl_heap_delete
+xl_heap_freeze_page
+xl_heap_freeze_tuple
+xl_heap_header
+xl_heap_inplace
+xl_heap_insert
+xl_heap_lock
+xl_heap_lock_updated
+xl_heap_multi_insert
+xl_heap_new_cid
+xl_heap_prune
+xl_heap_rewrite_mapping
+xl_heap_truncate
+xl_heap_update
+xl_heap_vacuum
+xl_heap_visible
+xl_invalid_page
+xl_invalid_page_key
+xl_invalidations
+xl_logical_message
+xl_multi_insert_tuple
+xl_multixact_create
+xl_multixact_truncate
+xl_overwrite_contrecord
+xl_parameter_change
+xl_relmap_update
+xl_replorigin_drop
+xl_replorigin_set
+xl_restore_point
+xl_running_xacts
+xl_seq_rec
+xl_smgr_create
+xl_smgr_truncate
+xl_standby_lock
+xl_standby_locks
+xl_tblspc_create_rec
+xl_tblspc_drop_rec
+xl_xact_abort
+xl_xact_assignment
+xl_xact_commit
+xl_xact_dbinfo
+xl_xact_invals
+xl_xact_origin
+xl_xact_parsed_abort
+xl_xact_parsed_commit
+xl_xact_parsed_prepare
+xl_xact_prepare
+xl_xact_relfilenodes
+xl_xact_stats_item
+xl_xact_stats_items
+xl_xact_subxacts
+xl_xact_twophase
+xl_xact_xinfo
+xmlBuffer
+xmlBufferPtr
+xmlChar
+xmlDocPtr
+xmlErrorPtr
+xmlExternalEntityLoader
+xmlGenericErrorFunc
+xmlNodePtr
+xmlNodeSetPtr
+xmlParserCtxtPtr
+xmlParserInputPtr
+xmlStructuredErrorFunc
+xmlTextWriter
+xmlTextWriterPtr
+xmlXPathCompExprPtr
+xmlXPathContextPtr
+xmlXPathObjectPtr
+xmltype
+xpath_workspace
+xsltSecurityPrefsPtr
+xsltStylesheetPtr
+xsltTransformContextPtr
+yy_parser
+yy_size_t
+yyscan_t
+z_stream
+z_streamp
+zic_t
diff --git a/src/tools/pgtest b/src/tools/pgtest
new file mode 100755
index 0000000..70f6a62
--- /dev/null
+++ b/src/tools/pgtest
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+# src/tools/pgtest [-n] [...]
+
+# This runs a build/initdb/regression test suite
+#
+# This will start a temporary postmaster, so you have to
+# have enough kernel resources to run two postmasters or
+# stop your main postmaster before running this script.
+#
+# Use -n to prevent 'make clean'
+
+MAKE="make"
+
+[ ! -d src ] && echo "This must be run from the top of the PostgreSQL source tree" 1>&2 && exit 1
+
+trap "rm -rf /tmp/$$" 0 1 2 3 15
+mkdir /tmp/$$
+TMP="/tmp/$$"
+
+if [ "X$1" != "X-n" ]
+then CLEAN="Y"
+else CLEAN=""
+ shift
+fi
+
+rm -f tmp_install/log/install.log
+
+# Run "make check" and store return code in $TMP/ret.
+# Display output but also capture it in $TMP/0.
+(
+ if [ "$CLEAN" ]
+ then $MAKE "$@" clean 2>&1
+ echo "$?" > $TMP/ret
+ fi
+ if [ $(cat $TMP/ret) -eq 0 ]
+ then $MAKE "$@" 2>&1 && $MAKE "$@" check 2>&1
+ echo "$?" > $TMP/ret
+ fi
+) | tee $TMP/0
+
+# Grab possible warnings from install.log
+[ -e tmp_install/log/install.log ] && cat tmp_install/log/install.log >> $TMP/0
+
+# If success, display warnings
+if [ $(cat $TMP/ret) -eq 0 ]
+then cat $TMP/0 |
+ # The following grep's have to be adjusted for your setup because
+ # certain warnings are acceptable.
+ grep -i warning |
+ grep -v setproctitle |
+ grep -v find_rule |
+ grep -v yy_flex_realloc
+fi
+
+# return original make error code
+exit `cat $TMP/ret`
diff --git a/src/tools/testint128.c b/src/tools/testint128.c
new file mode 100644
index 0000000..2ebd4f5
--- /dev/null
+++ b/src/tools/testint128.c
@@ -0,0 +1,170 @@
+/*-------------------------------------------------------------------------
+ *
+ * testint128.c
+ * Testbed for roll-our-own 128-bit integer arithmetic.
+ *
+ * This is a standalone test program that compares the behavior of an
+ * implementation in int128.h to an (assumed correct) int128 native type.
+ *
+ * Copyright (c) 2017-2022, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/tools/testint128.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+/*
+ * By default, we test the non-native implementation in int128.h; but
+ * by predefining USE_NATIVE_INT128 to 1, you can test the native
+ * implementation, just to be sure.
+ */
+#ifndef USE_NATIVE_INT128
+#define USE_NATIVE_INT128 0
+#endif
+
+#include "common/int128.h"
+#include "common/pg_prng.h"
+
+/*
+ * We assume the parts of this union are laid out compatibly.
+ */
+typedef union
+{
+ int128 i128;
+ INT128 I128;
+ union
+ {
+#ifdef WORDS_BIGENDIAN
+ int64 hi;
+ uint64 lo;
+#else
+ uint64 lo;
+ int64 hi;
+#endif
+ } hl;
+} test128;
+
+
+/*
+ * Control version of comparator.
+ */
+static inline int
+my_int128_compare(int128 x, int128 y)
+{
+ if (x < y)
+ return -1;
+ if (x > y)
+ return 1;
+ return 0;
+}
+
+/*
+ * Main program.
+ *
+ * Generates a lot of random numbers and tests the implementation for each.
+ * The results should be reproducible, since we use a fixed PRNG seed.
+ *
+ * You can give a loop count if you don't like the default 1B iterations.
+ */
+int
+main(int argc, char **argv)
+{
+ long count;
+
+ pg_prng_seed(&pg_global_prng_state, 0);
+
+ if (argc >= 2)
+ count = strtol(argv[1], NULL, 0);
+ else
+ count = 1000000000;
+
+ while (count-- > 0)
+ {
+ int64 x = pg_prng_uint64(&pg_global_prng_state);
+ int64 y = pg_prng_uint64(&pg_global_prng_state);
+ int64 z = pg_prng_uint64(&pg_global_prng_state);
+ test128 t1;
+ test128 t2;
+
+ /* check unsigned addition */
+ t1.hl.hi = x;
+ t1.hl.lo = y;
+ t2 = t1;
+ t1.i128 += (int128) (uint64) z;
+ int128_add_uint64(&t2.I128, (uint64) z);
+
+ if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
+ {
+ printf("%016lX%016lX + unsigned %lX\n", x, y, z);
+ printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
+ printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
+ return 1;
+ }
+
+ /* check signed addition */
+ t1.hl.hi = x;
+ t1.hl.lo = y;
+ t2 = t1;
+ t1.i128 += (int128) z;
+ int128_add_int64(&t2.I128, z);
+
+ if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
+ {
+ printf("%016lX%016lX + signed %lX\n", x, y, z);
+ printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
+ printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
+ return 1;
+ }
+
+ /* check multiplication */
+ t1.i128 = (int128) x * (int128) y;
+
+ t2.hl.hi = t2.hl.lo = 0;
+ int128_add_int64_mul_int64(&t2.I128, x, y);
+
+ if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
+ {
+ printf("%lX * %lX\n", x, y);
+ printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
+ printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
+ return 1;
+ }
+
+ /* check comparison */
+ t1.hl.hi = x;
+ t1.hl.lo = y;
+ t2.hl.hi = z;
+ t2.hl.lo = pg_prng_uint64(&pg_global_prng_state);
+
+ if (my_int128_compare(t1.i128, t2.i128) !=
+ int128_compare(t1.I128, t2.I128))
+ {
+ printf("comparison failure: %d vs %d\n",
+ my_int128_compare(t1.i128, t2.i128),
+ int128_compare(t1.I128, t2.I128));
+ printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
+ printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
+ return 1;
+ }
+
+ /* check case with identical hi parts; above will hardly ever hit it */
+ t2.hl.hi = x;
+
+ if (my_int128_compare(t1.i128, t2.i128) !=
+ int128_compare(t1.I128, t2.I128))
+ {
+ printf("comparison failure: %d vs %d\n",
+ my_int128_compare(t1.i128, t2.i128),
+ int128_compare(t1.I128, t2.I128));
+ printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
+ printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/tools/valgrind.supp b/src/tools/valgrind.supp
new file mode 100644
index 0000000..4e8c482
--- /dev/null
+++ b/src/tools/valgrind.supp
@@ -0,0 +1,182 @@
+# This is a suppression file for use with Valgrind tools. File format
+# documentation:
+# http://valgrind.org/docs/manual/mc-manual.html#mc-manual.suppfiles
+
+# The libc symbol that implements a particular standard interface is
+# implementation-dependent. For example, strncpy() shows up as "__GI_strncpy"
+# on some platforms. Use wildcards to avoid mentioning such specific names.
+# Avoid mentioning functions that are good candidates for inlining,
+# particularly single-caller static functions. Suppressions mentioning them
+# would be ineffective at higher optimization levels.
+
+
+# We have occasion to write raw binary structures to disk or to the network.
+# These may contain uninitialized padding bytes. Since recipients also ignore
+# those bytes as padding, this is harmless.
+
+{
+ padding_pgstat_write
+ Memcheck:Param
+ write(buf)
+
+ ...
+ fun:pgstat_write_statsfiles
+}
+
+{
+ padding_XLogRecData_CRC
+ Memcheck:Value8
+
+ fun:pg_comp_crc32c*
+ fun:XLogRecordAssemble
+}
+
+{
+ padding_XLogRecData_write
+ Memcheck:Param
+ pwrite64(buf)
+
+ ...
+ fun:XLogWrite
+}
+
+{
+ padding_relcache
+ Memcheck:Param
+ write(buf)
+
+ ...
+ fun:write_relcache_init_file
+}
+
+{
+ padding_reorderbuffer_serialize
+ Memcheck:Param
+ write(buf)
+
+ ...
+ fun:ReorderBufferSerializeTXN
+}
+
+{
+ padding_twophase_prepare
+ Memcheck:Param
+ write(buf)
+
+ ...
+ fun:EndPrepare
+}
+
+
+{
+ padding_twophase_CRC
+ Memcheck:Value8
+ fun:pg_comp_crc32c*
+ fun:EndPrepare
+}
+
+{
+ padding_bootstrap_initial_xlog_write
+ Memcheck:Param
+ write(buf)
+
+ ...
+ fun:BootStrapXLOG
+}
+
+{
+ padding_bootstrap_control_file_write
+ Memcheck:Param
+ write(buf)
+
+ ...
+ fun:WriteControlFile
+ fun:BootStrapXLOG
+}
+
+{
+ bootstrap_write_relmap_overlap
+ Memcheck:Overlap
+ fun:memcpy*
+ fun:write_relmap_file
+ fun:RelationMapFinishBootstrap
+}
+
+
+# gcc on ppc64 can generate a four-byte read to fetch the final "char" fields
+# of a FormData_pg_cast. This is valid compiler behavior, because a proper
+# FormData_pg_cast has trailing padding. Tuples we treat as structures omit
+# that padding, so Valgrind reports an invalid read. Practical trouble would
+# entail the missing pad bytes falling in a different memory page. So long as
+# the structure is aligned, that will not happen.
+{
+ overread_tuplestruct_pg_cast
+ Memcheck:Addr4
+
+ fun:IsBinaryCoercible
+}
+
+# Python's allocator does some low-level tricks for efficiency. Those
+# can be disabled for better instrumentation; but few people testing
+# postgres will have such a build of python. So add broad
+# suppressions of the resulting errors.
+# See also https://svn.python.org/projects/python/trunk/Misc/README.valgrind
+{
+ python_clever_allocator
+ Memcheck:Addr4
+ fun:PyObject_Free
+}
+
+{
+ python_clever_allocator
+ Memcheck:Addr8
+ fun:PyObject_Free
+}
+
+{
+ python_clever_allocator
+ Memcheck:Value4
+ fun:PyObject_Free
+}
+
+{
+ python_clever_allocator
+ Memcheck:Value8
+ fun:PyObject_Free
+}
+
+{
+ python_clever_allocator
+ Memcheck:Cond
+ fun:PyObject_Free
+}
+
+{
+ python_clever_allocator
+ Memcheck:Addr4
+ fun:PyObject_Realloc
+}
+
+{
+ python_clever_allocator
+ Memcheck:Addr8
+ fun:PyObject_Realloc
+}
+
+{
+ python_clever_allocator
+ Memcheck:Value4
+ fun:PyObject_Realloc
+}
+
+{
+ python_clever_allocator
+ Memcheck:Value8
+ fun:PyObject_Realloc
+}
+
+{
+ python_clever_allocator
+ Memcheck:Cond
+ fun:PyObject_Realloc
+}
diff --git a/src/tools/version_stamp.pl b/src/tools/version_stamp.pl
new file mode 100755
index 0000000..bf8fdfb
--- /dev/null
+++ b/src/tools/version_stamp.pl
@@ -0,0 +1,116 @@
+#! /usr/bin/perl
+
+#################################################################
+# version_stamp.pl -- update version stamps throughout the source tree
+#
+# Copyright (c) 2008-2022, PostgreSQL Global Development Group
+#
+# src/tools/version_stamp.pl
+#################################################################
+
+#
+# This script updates the version stamp in configure.ac, and also in assorted
+# other files wherein it's not convenient to obtain the version number from
+# configure's output. Note that you still have to run autoconf afterward
+# to regenerate configure from the updated configure.ac.
+#
+# Usage: cd to top of source tree and issue
+# src/tools/version_stamp.pl MINORVERSION
+# where MINORVERSION can be a minor release number (0, 1, etc), or
+# "devel", "alphaN", "betaN", "rcN".
+#
+
+use strict;
+use warnings;
+
+# Major version is hard-wired into the script. We update it when we branch
+# a new development version.
+my $majorversion = 15;
+
+# Validate argument and compute derived variables
+my $minor = shift;
+defined($minor) || die "$0: missing required argument: minor-version\n";
+
+my ($dotneeded);
+
+if ($minor =~ m/^\d+$/)
+{
+ $dotneeded = 1;
+}
+elsif ($minor eq "devel")
+{
+ $dotneeded = 0;
+}
+elsif ($minor =~ m/^alpha\d+$/)
+{
+ $dotneeded = 0;
+}
+elsif ($minor =~ m/^beta\d+$/)
+{
+ $dotneeded = 0;
+}
+elsif ($minor =~ m/^rc\d+$/)
+{
+ $dotneeded = 0;
+}
+else
+{
+ die "$0: minor-version must be N, devel, alphaN, betaN, or rcN\n";
+}
+
+my $fullversion;
+
+# Create various required forms of the version number
+if ($dotneeded)
+{
+ $fullversion = $majorversion . "." . $minor;
+}
+else
+{
+ $fullversion = $majorversion . $minor;
+}
+
+# Get the autoconf version number for eventual nag message
+# (this also ensures we're in the right directory)
+
+my $aconfver = "";
+open(my $fh, '<', "configure.ac") || die "could not read configure.ac: $!\n";
+while (<$fh>)
+{
+ if (m/^m4_if\(m4_defn\(\[m4_PACKAGE_VERSION\]\), \[(.*)\], \[\], \[m4_fatal/
+ )
+ {
+ $aconfver = $1;
+ last;
+ }
+}
+close($fh);
+$aconfver ne ""
+ || die "could not find autoconf version number in configure.ac\n";
+
+# Update configure.ac and other files that contain version numbers
+
+my $fixedfiles = "";
+
+sed_file("configure.ac",
+ "-e 's/AC_INIT(\\[PostgreSQL\\], \\[[0-9a-z.]*\\]/AC_INIT([PostgreSQL], [$fullversion]/'"
+);
+
+print "Stamped these files with version number $fullversion:\n$fixedfiles";
+print "Don't forget to run autoconf $aconfver before committing.\n";
+
+exit 0;
+
+sub sed_file
+{
+ my ($filename, $sedargs) = @_;
+ my ($tmpfilename) = $filename . ".tmp";
+
+ system("sed $sedargs $filename >$tmpfilename") == 0
+ or die "sed failed: $?";
+ system("mv $tmpfilename $filename") == 0
+ or die "mv failed: $?";
+
+ $fixedfiles .= "\t$filename\n";
+ return;
+}
diff --git a/src/tools/win32tzlist.pl b/src/tools/win32tzlist.pl
new file mode 100755
index 0000000..c43ab7e
--- /dev/null
+++ b/src/tools/win32tzlist.pl
@@ -0,0 +1,140 @@
+#################################################################
+#
+# win32tzlist.pl -- compare Windows timezone information
+#
+# Copyright (c) 2008-2022, PostgreSQL Global Development Group
+#
+# src/tools/win32tzlist.pl
+#################################################################
+
+#
+# This script compares the timezone information in the Windows registry
+# with that in src/bin/initdb/findtimezone.c. A list of changes will be
+# written to stdout - no attempt is made to automatically edit the file.
+#
+# Run the script from the top-level PG source directory.
+#
+
+use strict;
+use warnings;
+
+use Win32::Registry;
+
+my $tzfile = 'src/bin/initdb/findtimezone.c';
+
+#
+# Fetch all timezones in the registry
+#
+my $basekey;
+$HKEY_LOCAL_MACHINE->Open(
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones", $basekey)
+ or die $!;
+
+my @subkeys;
+$basekey->GetKeys(\@subkeys);
+
+my @system_zones;
+
+foreach my $keyname (@subkeys)
+{
+ my $subkey;
+ my %vals;
+
+ $basekey->Open($keyname, $subkey) or die $!;
+ $subkey->GetValues(\%vals) or die $!;
+ $subkey->Close();
+
+ die "Incomplete timezone data for $keyname!\n"
+ unless ($vals{Std} && $vals{Dlt} && $vals{Display});
+ push @system_zones,
+ {
+ 'std' => $vals{Std}->[2],
+ 'dlt' => $vals{Dlt}->[2],
+ 'display' => clean_displayname($vals{Display}->[2]),
+ };
+}
+
+$basekey->Close();
+
+#
+# Fetch all timezones currently in the file
+#
+my @file_zones;
+my $pgtz;
+open(my $tzfh, '<', $tzfile) or die "Could not open $tzfile!\n";
+{
+ local $/ = undef;
+ $pgtz = <$tzfh>;
+}
+close($tzfh);
+
+# Attempt to locate and extract the complete win32_tzmap struct
+$pgtz =~ /win32_tzmap\[\] =\s+{\s+\/\*[^\/]+\*\/\s+(.+?)};/gs
+ or die "Could not locate struct win32_tzmap in $tzfile!";
+$pgtz = $1;
+
+# Extract each individual record from the struct
+while ($pgtz =~
+ m/{\s+\/\*(.+?)\*\/\s+"([^"]+)",\s+"([^"]+)",\s+"([^"]+)",?\s+},/gs)
+{
+ push @file_zones,
+ {
+ 'display' => clean_displayname($1),
+ 'std' => $2,
+ 'dlt' => $3,
+ 'match' => $4,
+ };
+}
+
+#
+# Look for anything that has changed
+#
+my @add;
+
+for my $sys (@system_zones)
+{
+ my $match = 0;
+ for my $file (@file_zones)
+ {
+ if ($sys->{std} eq $file->{std})
+ {
+ $match = 1;
+ if ($sys->{dlt} ne $file->{dlt})
+ {
+ print
+ "Timezone $sys->{std}, changed name of daylight zone!\n";
+ }
+ if ($sys->{display} ne $file->{display})
+ {
+ print
+ "Timezone $sys->{std} changed displayname ('$sys->{display}' from '$file->{display}')!\n";
+ }
+ last;
+ }
+ }
+ unless ($match)
+ {
+ push @add, $sys;
+ }
+}
+
+if (@add)
+{
+ print "\n\nOther than that, add the following timezones:\n";
+ for my $z (@add)
+ {
+ print
+ "\t{\n\t\t/* $z->{display} */\n\t\t\"$z->{std}\", \"$z->{dlt}\",\n\t\t\"FIXME\"\n\t},\n";
+ }
+}
+
+sub clean_displayname
+{
+ my $dn = shift;
+
+ $dn =~ s/\*//gs;
+ $dn =~ s/\s+/ /gs;
+ $dn =~ s/^\s+//gs;
+ $dn =~ s/\s+$//gs;
+ return $dn;
+}