Subject: Re: Cannot find a working 64-bit integer type on Illumos

From: Thomas Munro <[email-masked]>
Date: Thu, 4 Jul 2024 14:55:59 +1200

On Wed, Jul 3, 2024 at 1:34 AM Heikki Linnakangas <[email-masked]> wrote:
> On 18/04/2024 23:29, Thomas Munro wrote:
> > On Thu, Apr 18, 2024 at 8:47 PM Peter Eisentraut <[email-masked]> wrote:
> >> I'm not sure I understand the problem here.  Do you mean that in theory
> >> a platform's PRId64 could be something other than "l" or "ll"?
> >
> > Yes.  I don't know why anyone would do that, and the systems I checked
> > all have the obvious definitions, eg "ld", "lld" etc.  Perhaps it's an
> > acceptable risk?  It certainly gives us a tidier result.
>
> Could we have a configure check or static assertion for that?

Unfortunately, that theory turned out to be wrong.  The usual suspect,
Windows, uses something else: "I64" or something like that.  We could
teach our snprintf to grok that, but I don't like the idea anymore.
So let's go back to INT64_MODIFIER, with just a small amount of
configure time work to pick the right value.  I couldn't figure out
any header-only way to do that.

> Personally, I find "PRId64" pretty unreadable. "INT64_MODIFIER" wasn't
> nice either, though, and following standards is good, so I'm sure I'll
> get used to it.

Yeah, I like standards a lot but we've painted ourselves into a corner here...

New version attached.  This time I was brave enough to try to tackle
src/timezone too, which had comments planning to drop a lot of small
differences against the upstream tzcode once all supported branches
required C99.  I suppose that should make future maintenance easier,
and C89 disappeared from our window of interest with PostgreSQL 11.
It's a little hard to understand what changed, but to try to show it
better I diff'd master against upstream (after filtering through sed
and pgindent as recommended by README), and then diff'd patched
against upstream, and then ... ehm.. diff'd the two diffs, so that you
can see there are some hunks that go away.

IMHO it's a rather scary choice on tzcode's part to use int_fastN_t,
and hard for us to verify that it works correctly especially when
combined with our changes, but on the other hand I don't really expect
any system that PostgreSQL can run on to have "fast" types that really
differ from the "exact width" types.  My understanding is that this is
something of interest to historical supercomputers and
microcontrollers, but I can't find any evidence of general
purpose/commodity systems that we target using different sizes (anyone
know better?).
--- zic.c-master-vs-upstream.diff	2024-07-04 14:38:42.919508000 +1200
+++ zic.c-patched-vs-upstream.diff	2024-07-04 14:37:23.663247000 +1200
@@ -1,6 +1,6 @@
 --- zic.filtered.c	2024-07-04 12:24:07.383657000 +1200
-+++ zic.c	2024-07-04 14:38:19.086696000 +1200
-@@ -3,104 +3,66 @@
++++ zic.c	2024-07-04 14:17:36.438520000 +1200
+@@ -3,104 +3,72 @@
  /*
   * This file is in the public domain, so clarified as of
   * 2006-07-17 by Arthur David Olson.
@@ -19,6 +19,8 @@
  
 -#include "version.h"
 +#include <fcntl.h>
++#include <inttypes.h>
++#include <stdint.h>
 +#include <sys/stat.h>
 +#include <time.h>
 +
@@ -36,20 +38,18 @@
 +#define	ZIC_VERSION_PRE_2013 '2'
 +#define	ZIC_VERSION	'3'
  
--typedef int_fast64_t zic_t;
--static zic_t const
--			ZIC_MIN = INT_FAST64_MIN,
--			ZIC_MAX = INT_FAST64_MAX,
+ typedef int_fast64_t zic_t;
+ static zic_t const
+ 			ZIC_MIN = INT_FAST64_MIN,
+ 			ZIC_MAX = INT_FAST64_MAX,
 -			ZIC32_MIN = -1 - (zic_t) 0x7fffffff,
 -			ZIC32_MAX = 0x7fffffff;
--#define SCNdZIC SCNdFAST64
-+typedef int64 zic_t;
-+#define ZIC_MIN PG_INT64_MIN
-+#define ZIC_MAX PG_INT64_MAX
++			ZIC32_MIN = INT32_MIN,
++			ZIC32_MAX = INT32_MAX;
+ #define SCNdZIC SCNdFAST64
  
  #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
--#define ZIC_MAX_ABBR_LEN_WO_WARN 6
-+#define ZIC_MAX_ABBR_LEN_WO_WARN	6
+ #define ZIC_MAX_ABBR_LEN_WO_WARN 6
  #endif							/* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
  
 -/* Minimum and maximum years, assuming signed 32-bit pg_time_t.  */
@@ -137,7 +137,7 @@
  	bool		r_hiwasnum;
  
  	int			r_month;		/* 0..11 */
-@@ -121,18 +83,16 @@
+@@ -121,18 +89,16 @@
  };
  
  /*
@@ -162,7 +162,7 @@
  	lineno_t	z_linenum;
  
  	const char *z_name;
-@@ -151,59 +111,46 @@
+@@ -151,59 +117,46 @@
  	zic_t		z_untiltime;
  };
  
@@ -245,7 +245,7 @@
  					const char *loyearp, const char *hiyearp,
  					const char *typep, const char *monthp,
  					const char *dayp, const char *timep);
-@@ -214,10 +161,21 @@
+@@ -214,10 +167,21 @@
  {
  PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1};
  
@@ -268,7 +268,7 @@
  static int	leapcnt;
  static bool leapseen;
  static zic_t leapminyear;
-@@ -228,132 +186,99 @@
+@@ -228,132 +192,99 @@
  static zic_t max_year;
  static zic_t min_year;
  static bool noise;
@@ -453,7 +453,7 @@
  
  static struct rule *rules;
  static ptrdiff_t nrules;		/* number of rules */
-@@ -365,7 +290,7 @@
+@@ -365,7 +296,7 @@
  
  struct link
  {
@@ -462,7 +462,7 @@
  	lineno_t	l_linenum;
  	const char *l_target;
  	const char *l_linkname;
-@@ -381,8 +306,8 @@
+@@ -381,8 +312,8 @@
  	const int	l_value;
  };
  
@@ -473,7 +473,7 @@
  
  static struct lookup const zi_line_codes[] = {
  	{"Rule", LC_RULE},
-@@ -436,10 +361,12 @@
+@@ -436,10 +367,12 @@
  
  static struct lookup const begin_years[] = {
  	{"minimum", YR_MINIMUM},
@@ -486,7 +486,7 @@
  	{"maximum", YR_MAXIMUM},
  	{"only", YR_ONLY},
  	{NULL, 0}
-@@ -480,79 +407,30 @@
+@@ -480,79 +413,30 @@
   * Memory allocation.
   */
  
@@ -574,7 +574,7 @@
  emalloc(size_t size)
  {
  	return memcheck(malloc(size));
-@@ -564,100 +442,66 @@
+@@ -564,100 +448,66 @@
  	return memcheck(realloc(ptr, size));
  }
  
@@ -703,7 +703,7 @@
  {
  	va_list		args;
  
-@@ -667,8 +511,8 @@
+@@ -667,8 +517,8 @@
  	errors = true;
  }
  
@@ -714,7 +714,7 @@
  {
  	va_list		args;
  
-@@ -679,11 +523,8 @@
+@@ -679,11 +529,8 @@
  	warnings = true;
  }
  
@@ -727,7 +727,7 @@
  {
  	char const *e = (ferror(stream) ? _("I/O error")
  					 : fclose(stream) != 0 ? strerror(errno) : NULL);
-@@ -694,26 +535,23 @@
+@@ -694,26 +541,23 @@
  				dir ? dir : "", dir ? "/" : "",
  				name ? name : "", name ? ": " : "",
  				e);
@@ -759,7 +759,7 @@
  	exit(status);
  }
  
-@@ -741,221 +579,8 @@
+@@ -741,221 +585,8 @@
  	}
  }
  
@@ -982,7 +982,7 @@
  /* The minimum and maximum values representable in a TZif file.  */
  static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
  static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
-@@ -965,19 +590,18 @@
+@@ -965,13 +596,12 @@
  static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
  static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
  
@@ -999,32 +999,8 @@
  /* Set the time range of the output to TIMERANGE.
     Return true if successful.  */
  static bool
- timerange_option(char *timerange)
- {
--	intmax_t	lo = min_time,
-+	int64		lo = min_time,
- 				hi = max_time;
- 	char	   *lo_end = timerange,
- 			   *hi_end;
-@@ -986,7 +610,7 @@
- 	{
- 		errno = 0;
- 		lo = strtoimax(timerange + 1, &lo_end, 10);
--		if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE))
-+		if (lo_end == timerange + 1 || (lo == PG_INT64_MAX && errno == ERANGE))
- 			return false;
+@@ -1000,33 +630,15 @@
  	}
- 	hi_end = lo_end;
-@@ -994,39 +618,21 @@
- 	{
- 		errno = 0;
- 		hi = strtoimax(lo_end + 2, &hi_end, 10);
--		if (hi_end == lo_end + 2 || hi == INTMAX_MIN)
-+		if (hi_end == lo_end + 2 || hi == PG_INT64_MIN)
- 			return false;
--		hi -= !(hi == INTMAX_MAX && errno == ERANGE);
-+		hi -= !(hi == PG_INT64_MAX && errno == ERANGE);
- 	}
  	if (*hi_end || hi < lo || max_time < lo || hi < min_time)
  		return false;
 -	lo_time = max(lo, min_time);
@@ -1060,7 +1036,7 @@
  static const char *tzdefault;
  
  /* -1 if the TZif output file should be slim, 0 if default, 1 if the
-@@ -1053,18 +659,10 @@
+@@ -1053,18 +665,10 @@
  				j;
  	bool		timerange_given = false;
  
@@ -1081,7 +1057,7 @@
  	if (TYPE_BIT(zic_t) < 64)
  	{
  		fprintf(stderr, "%s: %s\n", progname,
-@@ -1074,16 +672,15 @@
+@@ -1074,16 +678,15 @@
  	for (k = 1; k < argc; k++)
  		if (strcmp(argv[k], "--version") == 0)
  		{
@@ -1101,7 +1077,7 @@
  		switch (c)
  		{
  			default:
-@@ -1106,36 +703,33 @@
+@@ -1106,36 +709,33 @@
  				break;
  			case 'd':
  				if (directory == NULL)
@@ -1144,7 +1120,7 @@
  							progname);
  					return EXIT_FAILURE;
  				}
-@@ -1156,12 +750,11 @@
+@@ -1156,12 +756,11 @@
  				break;
  			case 'L':
  				if (leapsec == NULL)
@@ -1159,7 +1135,7 @@
  							progname);
  					return EXIT_FAILURE;
  				}
-@@ -1169,12 +762,15 @@
+@@ -1169,12 +768,15 @@
  			case 'v':
  				noise = true;
  				break;
@@ -1177,7 +1153,7 @@
  							progname);
  					return EXIT_FAILURE;
  				}
-@@ -1187,27 +783,12 @@
+@@ -1187,27 +789,12 @@
  				}
  				timerange_given = true;
  				break;
@@ -1205,7 +1181,7 @@
  	if (bloat == 0)
  	{
  		static char const bloat_default[] = ZIC_BLOAT_DEFAULT;
-@@ -1220,23 +801,22 @@
+@@ -1220,23 +807,22 @@
  			abort();			/* Configuration error.  */
  	}
  	if (directory == NULL)
@@ -1232,7 +1208,7 @@
  	for (i = 0; i < nzones; i = j)
  	{
  		/*
-@@ -1246,15 +826,28 @@
+@@ -1246,15 +832,28 @@
  			continue;
  		outzone(&zones[i], j - i);
  	}
@@ -1264,7 +1240,7 @@
  		dolink(psxrules, TZDEFRULES, true);
  	}
  	if (warnings && (ferror(stderr) || fclose(stderr) != 0))
-@@ -1347,211 +940,22 @@
+@@ -1347,211 +946,22 @@
  	return componentcheck(name, component, cp);
  }
  
@@ -1487,7 +1463,7 @@
  	char const *f = target;
  	char	   *result = NULL;
  
-@@ -1559,14 +963,13 @@
+@@ -1559,14 +969,13 @@
  	{
  		/* Make F absolute too.  */
  		size_t		len = strlen(directory);
@@ -1506,7 +1482,7 @@
  	}
  	for (i = 0; f[i] && f[i] == linkname[i]; i++)
  		if (f[i] == '/')
-@@ -1574,7 +977,7 @@
+@@ -1574,7 +983,7 @@
  	for (; linkname[i]; i++)
  		dotdots += linkname[i] == '/' && linkname[i - 1] != '/';
  	taillen = strlen(f + dir_len);
@@ -1515,7 +1491,7 @@
  	if (dotdotetcsize <= linksize)
  	{
  		if (!result)
-@@ -1585,133 +988,80 @@
+@@ -1585,133 +994,80 @@
  	}
  	return result;
  }
@@ -1693,7 +1669,7 @@
  		{
  			FILE	   *fp,
  					   *tp;
-@@ -1726,37 +1076,69 @@
+@@ -1726,37 +1082,69 @@
  						progname, directory, target, e);
  				exit(EXIT_FAILURE);
  			}
@@ -1776,7 +1752,7 @@
  /*
   * Associate sets of rules with zones.
   */
-@@ -1768,10 +1150,8 @@
+@@ -1768,10 +1156,8 @@
  static int
  rcomp(const void *cp1, const void *cp2)
  {
@@ -1789,7 +1765,7 @@
  }
  
  static void
-@@ -1784,7 +1164,7 @@
+@@ -1784,7 +1170,7 @@
  				base,
  				out;
  
@@ -1798,7 +1774,7 @@
  	{
  		qsort(rules, nrules, sizeof *rules, rcomp);
  		for (i = 0; i < nrules - 1; ++i)
-@@ -1792,21 +1172,23 @@
+@@ -1792,21 +1178,23 @@
  			if (strcmp(rules[i].r_name,
  					   rules[i + 1].r_name) != 0)
  				continue;
@@ -1828,7 +1804,7 @@
  					continue;
  				break;
  			}
-@@ -1842,7 +1224,7 @@
+@@ -1842,7 +1230,7 @@
  			/*
  			 * Maybe we have a local standard time offset.
  			 */
@@ -1837,7 +1813,7 @@
  			zp->z_save = getsave(zp->z_rule, &zp->z_isdst);
  
  			/*
-@@ -1857,85 +1239,71 @@
+@@ -1857,85 +1245,60 @@
  		exit(EXIT_FAILURE);
  }
  
@@ -1947,23 +1923,12 @@
 -			/* nothing to do */
 +			if (name == leapsec && *buf == '#')
 +			{
-+				/*
-+				 * PG: INT64_FORMAT isn't portable for sscanf, so be content
-+				 * with scanning a "long".  Once we are requiring C99 in all
-+				 * live branches, it'd be sensible to adopt upstream's
-+				 * practice of using the <inttypes.h> macros.  But for now, we
-+				 * don't actually use this code, and it won't overflow before
-+				 * 2038 anyway.
-+				 */
-+				long		cl_tmp;
-+
-+				sscanf(buf, "#expires %ld", &cl_tmp);
-+				comment_leapexpires = cl_tmp;
++				sscanf(buf, "#expires %" SCNdZIC, &comment_leapexpires);
 +			}
  		}
  		else if (wantcont)
  		{
-@@ -1944,7 +1312,7 @@
+@@ -1944,7 +1307,7 @@
  		else
  		{
  			struct lookup const *line_codes
@@ -1972,7 +1937,7 @@
  
  			lp = byword(fields[0], line_codes);
  			if (lp == NULL)
-@@ -1971,12 +1339,16 @@
+@@ -1971,12 +1334,16 @@
  						inexpires(fields, nfields);
  						wantcont = false;
  						break;
@@ -1992,26 +1957,7 @@
  	if (wantcont)
  		error(_("expected continuation line not found"));
  }
-@@ -1992,7 +1364,8 @@
- static zic_t
- gethms(char const *string, char const *errstring)
- {
--	zic_t		hh;
-+	/* PG: make hh be int not zic_t to avoid sscanf portability issues */
-+	int			hh;
- 	int			sign,
- 				mm = 0,
- 				ss = 0;
-@@ -2014,7 +1387,7 @@
- 	else
- 		sign = 1;
- 	switch (sscanf(string,
--				   "%" SCNdZIC "%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
-+				   "%d%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
- 				   &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs))
- 	{
- 		default:
-@@ -2022,19 +1395,19 @@
+@@ -2022,19 +1389,19 @@
  			break;
  		case 8:
  			ok = '0' <= xr && xr <= '9';
@@ -2035,12 +1981,12 @@
  		case 1:
  			break;
  	}
-@@ -2050,16 +1423,19 @@
+@@ -2050,16 +1417,19 @@
  		error("%s", errstring);
  		return 0;
  	}
 +	/* Some compilers warn that this test is unsatisfiable for 32-bit ints */
-+#if INT_MAX > PG_INT32_MAX
++#if INT_MAX > INT32_MAX
  	if (ZIC_MAX / SECSPERHOUR < hh)
  	{
  		error(_("time overflow"));
@@ -2056,7 +2002,7 @@
  				sign * (mm * SECSPERMIN + ss));
  }
  
-@@ -2068,7 +1444,7 @@
+@@ -2068,7 +1438,7 @@
  {
  	int			dst = -1;
  	zic_t		save;
@@ -2065,7 +2011,7 @@
  
  	if (fieldlen != 0)
  	{
-@@ -2094,7 +1470,7 @@
+@@ -2094,7 +1464,7 @@
  static void
  inrule(char **fields, int nfields)
  {
@@ -2074,7 +2020,7 @@
  
  	if (nfields != RULE_FIELDS)
  	{
-@@ -2125,15 +1501,13 @@
+@@ -2125,15 +1495,13 @@
  			error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
  			return;
  	}
@@ -2095,7 +2041,7 @@
  	if (max_abbrvar_len < strlen(r.r_abbrvar))
  		max_abbrvar_len = strlen(r.r_abbrvar);
  	rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
-@@ -2152,13 +1526,15 @@
+@@ -2152,13 +1520,15 @@
  	}
  	if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0)
  	{
@@ -2113,7 +2059,7 @@
  			  TZDEFRULES);
  		return false;
  	}
-@@ -2167,9 +1543,9 @@
+@@ -2167,9 +1537,9 @@
  			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0)
  		{
  			error(_("duplicate zone name %s"
@@ -2125,7 +2071,7 @@
  				  zones[i].z_linenum);
  			return false;
  		}
-@@ -2192,8 +1568,7 @@
+@@ -2192,8 +1562,7 @@
  {
  	char	   *cp;
  	char	   *cp1;
@@ -2135,7 +2081,7 @@
  	int			i_stdoff,
  				i_rule,
  				i_format;
-@@ -2212,6 +1587,7 @@
+@@ -2212,6 +1581,7 @@
  		i_untilmonth = ZFC_TILMONTH;
  		i_untilday = ZFC_TILDAY;
  		i_untiltime = ZFC_TILTIME;
@@ -2143,7 +2089,7 @@
  	}
  	else if (!namecheck(fields[ZF_NAME]))
  		return false;
-@@ -2224,11 +1600,12 @@
+@@ -2224,11 +1594,12 @@
  		i_untilmonth = ZF_TILMONTH;
  		i_untilday = ZF_TILDAY;
  		i_untiltime = ZF_TILTIME;
@@ -2158,7 +2104,7 @@
  	{
  		if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
  			|| strchr(fields[i_format], '/'))
-@@ -2237,25 +1614,31 @@
+@@ -2237,25 +1608,31 @@
  			return false;
  		}
  	}
@@ -2204,7 +2150,7 @@
  		z.z_untiltime = rpytime(&z.z_untilrule,
  								z.z_untilrule.r_loyear);
  		if (iscont && nzones > 0 &&
-@@ -2265,21 +1648,10 @@
+@@ -2265,21 +1642,10 @@
  			zones[nzones - 1].z_untiltime < max_time &&
  			zones[nzones - 1].z_untiltime >= z.z_untiltime)
  		{
@@ -2227,7 +2173,7 @@
  	zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
  	zones[nzones++] = z;
  
-@@ -2291,13 +1663,15 @@
+@@ -2291,12 +1657,13 @@
  }
  
  static zic_t
@@ -2238,23 +2184,11 @@
  	const struct lookup *lp;
  	zic_t		i,
  				j;
--	zic_t		year;
 +
-+	/* PG: make year be int not zic_t to avoid sscanf portability issues */
-+	int			year;
+ 	zic_t		year;
  	int			month,
  				day;
- 	zic_t		dayoff,
-@@ -2307,7 +1681,7 @@
- 
- 	dayoff = 0;
- 	cp = fields[LP_YEAR];
--	if (sscanf(cp, "%" SCNdZIC "%c", &year, &xs) != 1)
-+	if (sscanf(cp, "%d%c", &year, &xs) != 1)
- 	{
- 		/*
- 		 * Leapin' Lizards!
-@@ -2384,7 +1758,7 @@
+@@ -2384,7 +1751,7 @@
  		error(_("wrong number of fields on Leap line"));
  	else
  	{
@@ -2263,7 +2197,7 @@
  
  		if (0 <= t)
  		{
-@@ -2417,7 +1791,7 @@
+@@ -2417,7 +1784,7 @@
  	else if (0 <= leapexpires)
  		error(_("multiple Expires lines"));
  	else
@@ -2272,7 +2206,7 @@
  }
  
  static void
-@@ -2437,15 +1811,15 @@
+@@ -2437,15 +1804,15 @@
  	}
  	if (!namecheck(fields[LF_LINKNAME]))
  		return;
@@ -2292,13 +2226,7 @@
  rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
  		const char *typep, const char *monthp, const char *dayp,
  		const char *timep)
-@@ -2456,15 +1830,18 @@
- 	char	   *ep;
- 	char		xs;
- 
-+	/* PG: year_tmp is to avoid sscanf portability issues */
-+	int			year_tmp;
-+
+@@ -2459,12 +1826,12 @@
  	if ((lp = byword(monthp, mon_names)) == NULL)
  	{
  		error(_("invalid month name"));
@@ -2313,7 +2241,7 @@
  	if (*dp != '\0')
  	{
  		ep = dp + strlen(dp) - 1;
-@@ -2497,22 +1874,28 @@
+@@ -2497,22 +1864,26 @@
  	 */
  	cp = loyearp;
  	lp = byword(cp, begin_years);
@@ -2340,10 +2268,7 @@
 +						progname, lp->l_value);
 +				exit(EXIT_FAILURE);
  		}
--	else if (sscanf(cp, "%" SCNdZIC "%c", &rp->r_loyear, &xs) != 1)
-+	else if (sscanf(cp, "%d%c", &year_tmp, &xs) == 1)
-+		rp->r_loyear = year_tmp;
-+	else
+ 	else if (sscanf(cp, "%" SCNdZIC "%c", &rp->r_loyear, &xs) != 1)
  	{
  		error(_("invalid starting year"));
 -		return false;
@@ -2351,7 +2276,7 @@
  	}
  	cp = hiyearp;
  	lp = byword(cp, end_years);
-@@ -2520,37 +1903,45 @@
+@@ -2520,37 +1891,43 @@
  	if (!rp->r_hiwasnum)
  		switch (lp->l_value)
  		{
@@ -2372,10 +2297,7 @@
 +						progname, lp->l_value);
 +				exit(EXIT_FAILURE);
  		}
--	else if (sscanf(cp, "%" SCNdZIC "%c", &rp->r_hiyear, &xs) != 1)
-+	else if (sscanf(cp, "%d%c", &year_tmp, &xs) == 1)
-+		rp->r_hiyear = year_tmp;
-+	else
+ 	else if (sscanf(cp, "%" SCNdZIC "%c", &rp->r_hiyear, &xs) != 1)
  	{
  		error(_("invalid ending year"));
 -		return false;
@@ -2404,7 +2326,7 @@
  	if ((lp = byword(dp, lasts)) != NULL)
  	{
  		rp->r_dycode = DC_DOWLEQ;
-@@ -2559,9 +1950,9 @@
+@@ -2559,9 +1936,9 @@
  	}
  	else
  	{
@@ -2416,7 +2338,7 @@
  			rp->r_dycode = DC_DOWGEQ;
  		else
  		{
-@@ -2575,13 +1966,13 @@
+@@ -2575,13 +1952,13 @@
  			{
  				error(_("invalid day of month"));
  				free(dp);
@@ -2432,7 +2354,7 @@
  			}
  			rp->r_wday = lp->l_value;
  		}
-@@ -2591,37 +1982,36 @@
+@@ -2591,37 +1968,36 @@
  		{
  			error(_("invalid day of month"));
  			free(dp);
@@ -2446,7 +2368,7 @@
  
  static void
 -convert(uint_fast32_t val, char *buf)
-+convert(const int32 val, char *const buf)
++convert(const int_fast32_t val, char *const buf)
  {
  	int			i;
  	int			shift;
@@ -2459,7 +2381,7 @@
  
  static void
 -convert64(uint_fast64_t val, char *buf)
-+convert64(const zic_t val, char *const buf)
++convert64(uint_fast64_t val, char *const buf)
  {
  	int			i;
  	int			shift;
@@ -2472,11 +2394,11 @@
  
  static void
 -puttzcode(zic_t val, FILE *fp)
-+puttzcode(const int32 val, FILE *const fp)
++puttzcode(zic_t val, FILE *const fp)
  {
  	char		buf[4];
  
-@@ -2646,12 +2036,10 @@
+@@ -2646,12 +2022,10 @@
  static int
  atcomp(const void *avp, const void *bvp)
  {
@@ -2492,7 +2414,7 @@
  }
  
  struct timerange
-@@ -2661,44 +2049,25 @@
+@@ -2661,44 +2035,25 @@
  				count;
  	int			leapbase,
  				leapcount;
@@ -2539,7 +2461,7 @@
  	{
  		while (0 < r.count && hi + 1 < ats[r.base + r.count - 1])
  			r.count--;
-@@ -2706,9 +2075,6 @@
+@@ -2706,9 +2061,6 @@
  			r.leapcount--;
  	}
  
@@ -2549,7 +2471,7 @@
  	return r;
  }
  
-@@ -2720,20 +2086,23 @@
+@@ -2720,20 +2072,23 @@
  	ptrdiff_t	i,
  				j;
  	int			pass;
@@ -2582,7 +2504,7 @@
  
  	/*
  	 * Sort.
-@@ -2810,49 +2179,61 @@
+@@ -2810,49 +2165,61 @@
  			}
  	}
  
@@ -2611,9 +2533,8 @@
 -						 max(hi_time,
 -							 redundant_time - (ZIC_MIN < redundant_time)),
 -						 ats, types);
--	range32 = limitrange(range64, ZIC32_MIN, ZIC32_MAX, ats, types);
 +	range64 = limitrange(rangeall, lo_time, hi_time, ats, types);
-+	range32 = limitrange(range64, PG_INT32_MIN, PG_INT32_MAX, ats, types);
+ 	range32 = limitrange(range64, ZIC32_MIN, ZIC32_MAX, ats, types);
  
  	/*
 -	 * TZif version 4 is needed if a no-op transition is appended to indicate
@@ -2674,7 +2595,7 @@
  	for (pass = 1; pass <= 2; ++pass)
  	{
  		ptrdiff_t	thistimei,
-@@ -2861,15 +2242,11 @@
+@@ -2861,15 +2228,11 @@
  		int			thisleapi,
  					thisleapcnt,
  					thisleaplim;
@@ -2693,7 +2614,7 @@
  		int			old0;
  		char		omittype[TZ_MAX_TYPES];
  		int			typemap[TZ_MAX_TYPES];
-@@ -2883,15 +2260,28 @@
+@@ -2883,15 +2246,28 @@
  
  		if (pass == 1)
  		{
@@ -2701,7 +2622,7 @@
 +			/*
 +			 * Arguably the default time type in the 32-bit data should be
 +			 * range32.defaulttype, which is suited for timestamps just before
-+			 * PG_INT32_MIN.  However, zic traditionally used the time type of
++			 * INT32_MIN.  However, zic traditionally used the time type of
 +			 * the indefinite past instead.  Internet RFC 8532 says readers
 +			 * should ignore 32-bit data, so this discrepancy matters only to
 +			 * obsolete readers where the traditional type might be more
@@ -2709,7 +2630,7 @@
 +			 * value, unless -r specifies a low cutoff that excludes some
 +			 * 32-bit timestamps.
 +			 */
-+			thisdefaulttype = (lo_time <= PG_INT32_MIN
++			thisdefaulttype = (lo_time <= INT32_MIN
 +							   ? range64.defaulttype
 +							   : range32.defaulttype);
 +
@@ -2721,12 +2642,12 @@
 -			thisleapexpiry = range32.leapexpiry;
 -			thismin = ZIC32_MIN;
 -			thismax = ZIC32_MAX;
-+			locut = PG_INT32_MIN < lo_time;
-+			hicut = hi_time < PG_INT32_MAX;
++			locut = INT32_MIN < lo_time;
++			hicut = hi_time < INT32_MAX;
  		}
  		else
  		{
-@@ -2901,52 +2291,40 @@
+@@ -2901,52 +2277,40 @@
  			toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
  			thisleapi = range64.leapbase;
  			thisleapcnt = range64.leapcount;
@@ -2801,7 +2722,7 @@
  
  		/*
  		 * Reorder types to make THISDEFAULTTYPE type 0. Use TYPEMAP to swap
-@@ -2972,14 +2350,7 @@
+@@ -2972,14 +2336,7 @@
  						type;
  
  			hidst = histd = mrudst = mrustd = -1;
@@ -2817,7 +2738,7 @@
  				if (isdsts[types[i]])
  					mrudst = types[i];
  				else
-@@ -3059,20 +2430,19 @@
+@@ -3059,20 +2416,19 @@
  		}
  		if (pass == 1 && !want_bloat())
  		{
@@ -2844,11 +2765,16 @@
  		convert(thistypecnt, tzh.tzh_typecnt);
  		convert(thischarcnt, tzh.tzh_charcnt);
  		DO(tzh_magic);
-@@ -3095,26 +2465,62 @@
- 			continue;
+@@ -3096,25 +2452,67 @@
  		}
  
-+		/* PG: print current timezone abbreviations if requested */
+ 		/*
++		 * PG: print current timezone abbreviations if requested.  Note that
++		 * we cast to int64_t, because int_fast64_t is not required to be
++		 * compatible with INT64_FORMAT.  We can't use PRIdFAST64 here because
++		 * we override fprintf with our own implementation that might not
++		 * understand it.
++		 */
 +		if (print_abbrevs && pass == 2)
 +		{
 +			/* Print "type" data for periods ending after print_cutoff */
@@ -2861,7 +2787,7 @@
 +
 +					fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
 +							thisabbrev,
-+							utoffs[tm],
++							(int64_t) utoffs[tm],
 +							isdsts[tm] ? "\tD" : "");
 +				}
 +			}
@@ -2873,17 +2799,16 @@
 +
 +				fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
 +						thisabbrev,
-+						utoffs[tm],
++						(int64_t) utoffs[tm],
 +						isdsts[tm] ? "\tD" : "");
 +			}
 +		}
 +
- 		/*
++		/*
  		 * Output a LO_TIME transition if needed; see limitrange. But do not
  		 * go below the minimum representable value for this pass.
  		 */
--		lo = pass == 1 && lo_time < ZIC32_MIN ? ZIC32_MIN : lo_time;
-+		lo = pass == 1 && lo_time < PG_INT32_MIN ? PG_INT32_MIN : lo_time;
+ 		lo = pass == 1 && lo_time < ZIC32_MIN ? ZIC32_MIN : lo_time;
  
 -		if (0 <= pretranstype)
 +		if (locut)
@@ -2915,7 +2840,7 @@
  
  		for (i = old0; i < typecnt; i++)
  		{
-@@ -3131,7 +2537,6 @@
+@@ -3131,7 +2529,6 @@
  		if (thischarcnt != 0)
  			fwrite(thischars, sizeof thischars[0],
  				   thischarcnt, fp);
@@ -2923,7 +2848,7 @@
  		for (i = thisleapi; i < thisleaplim; ++i)
  		{
  			zic_t		todo;
-@@ -3163,17 +2568,6 @@
+@@ -3163,17 +2560,6 @@
  			puttzcodepass(todo, fp, pass);
  			puttzcode(corr[i], fp);
  		}
@@ -2941,7 +2866,7 @@
  		if (stdcnt != 0)
  			for (i = old0; i < typecnt; i++)
  				if (!omittype[i])
-@@ -3184,8 +2578,7 @@
+@@ -3184,8 +2570,7 @@
  					putc(ttisuts[i], fp);
  	}
  	fprintf(fp, "\n%s\n", string);
@@ -2951,7 +2876,7 @@
  	free(ats);
  }
  
-@@ -3233,15 +2626,13 @@
+@@ -3233,15 +2618,13 @@
  	}
  }
  
@@ -2969,7 +2894,7 @@
  	char const *format = zp->z_format;
  
  	slashp = strchr(format, '/');
-@@ -3253,8 +2644,6 @@
+@@ -3253,8 +2636,6 @@
  			letters = abbroffset(letterbuf, zp->z_stdoff + save);
  		else if (!letters)
  			letters = "%s";
@@ -2978,7 +2903,7 @@
  		sprintf(abbr, format, letters);
  	}
  	else if (isdst)
-@@ -3413,17 +2802,11 @@
+@@ -3413,17 +2794,11 @@
  		return 1;
  	if (a->r_hiyear != b->r_hiyear)
  		return a->r_hiyear < b->r_hiyear ? -1 : 1;
@@ -2996,7 +2921,7 @@
  static int
  stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
  {
-@@ -3432,17 +2815,13 @@
+@@ -3432,17 +2807,13 @@
  	struct rule *stdrp;
  	struct rule *dstrp;
  	ptrdiff_t	i;
@@ -3016,7 +2941,7 @@
  
  	result[0] = '\0';
  
-@@ -3454,74 +2833,72 @@
+@@ -3454,74 +2825,72 @@
  		return -1;
  
  	zp = zpfirst + zonecount - 1;
@@ -3146,7 +3071,7 @@
  	if (!offsetlen)
  	{
  		result[0] = '\0';
-@@ -3530,12 +2907,12 @@
+@@ -3530,12 +2899,12 @@
  	len += offsetlen;
  	if (dstrp == NULL)
  		return compat;
@@ -3161,7 +3086,7 @@
  		if (!offsetlen)
  		{
  			result[0] = '\0';
-@@ -3544,7 +2921,7 @@
+@@ -3544,7 +2913,7 @@
  		len += offsetlen;
  	}
  	result[len++] = ',';
@@ -3170,7 +3095,7 @@
  	if (c < 0)
  	{
  		result[0] = '\0';
-@@ -3554,7 +2931,7 @@
+@@ -3554,7 +2923,7 @@
  		compat = c;
  	len += strlen(result + len);
  	result[len++] = ',';
@@ -3179,7 +3104,7 @@
  	if (c < 0)
  	{
  		result[0] = '\0';
-@@ -3568,31 +2945,38 @@
+@@ -3568,31 +2937,38 @@
  static void
  outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
  {
@@ -3224,7 +3149,7 @@
  	startbuf = emalloc(max_abbr_len + 1);
  	ab = emalloc(max_abbr_len + 1);
  	envvar = emalloc(max_envvar_len + 1);
-@@ -3605,6 +2989,7 @@
+@@ -3605,6 +2981,7 @@
  	timecnt = 0;
  	typecnt = 0;
  	charcnt = 0;
@@ -3232,7 +3157,7 @@
  
  	/*
  	 * Thanks to Earl Chew for noting the need to unconditionally initialize
-@@ -3620,17 +3005,18 @@
+@@ -3620,17 +2997,18 @@
  	}
  	for (i = 0; i < zonecount; ++i)
  	{
@@ -3256,7 +3181,7 @@
  		}
  	}
  
-@@ -3638,14 +3024,13 @@
+@@ -3638,14 +3016,13 @@
  	 * Generate lots of data if a rule can't cover all future times.
  	 */
  	compat = stringzone(envvar, zpfirst, zonecount);
@@ -3273,7 +3198,7 @@
  					zpfirst->z_name);
  		else if (compat != 0)
  		{
-@@ -3660,6 +3045,22 @@
+@@ -3660,6 +3037,22 @@
  	}
  	if (do_extend)
  	{
@@ -3296,7 +3221,7 @@
  		if (min_year >= ZIC_MIN + years_of_observations)
  			min_year -= years_of_observations;
  		else
-@@ -3668,9 +3069,18 @@
+@@ -3668,9 +3061,18 @@
  			max_year += years_of_observations;
  		else
  			max_year = ZIC_MAX;
@@ -3317,7 +3242,7 @@
  	max_year0 = max_year;
  	if (want_bloat())
  	{
-@@ -3678,35 +3088,31 @@
+@@ -3678,35 +3080,31 @@
  		 * For the benefit of older systems, generate data from 1900 through
  		 * 2038.
  		 */
@@ -3366,7 +3291,7 @@
  			save = zp->z_save;
  			doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
  			type = addtype(oadd(zp->z_stdoff, save),
-@@ -3715,20 +3121,12 @@
+@@ -3715,20 +3113,12 @@
  			if (usestart)
  			{
  				addtt(starttime, type);
@@ -3387,7 +3312,7 @@
  			for (year = min_year; year <= max_year; ++year)
  			{
  				if (useuntil && year > zp->z_untilrule.r_hiyear)
-@@ -3741,12 +3139,9 @@
+@@ -3741,12 +3131,9 @@
  				 */
  				for (j = 0; j < zp->z_nrules; ++j)
  				{
@@ -3403,7 +3328,7 @@
  					rp->r_todo = year >= rp->r_loyear &&
  						year <= rp->r_hiyear;
  					if (rp->r_todo)
-@@ -3763,8 +3158,6 @@
+@@ -3763,8 +3150,6 @@
  					zic_t		jtime,
  								ktime;
  					zic_t		offset;
@@ -3412,7 +3337,7 @@
  
  					INITIALIZE(ktime);
  					if (useuntil)
-@@ -3789,16 +3182,15 @@
+@@ -3789,16 +3174,15 @@
  					k = -1;
  					for (j = 0; j < zp->z_nrules; ++j)
  					{
@@ -3436,7 +3361,7 @@
  						if (jtime == min_time ||
  							jtime == max_time)
  							continue;
-@@ -3813,12 +3205,12 @@
+@@ -3813,12 +3197,12 @@
  							char const *dup_rules_msg =
  								_("two rules for same instant");
  
@@ -3454,7 +3379,7 @@
  							error("%s", dup_rules_msg);
  						}
  					}
-@@ -3827,15 +3219,7 @@
+@@ -3827,15 +3211,7 @@
  					rp = &zp->z_rules[k];
  					rp->r_todo = false;
  					if (useuntil && ktime >= untiltime)
@@ -3470,7 +3395,7 @@
  					save = rp->r_save;
  					if (usestart && ktime == starttime)
  						usestart = false;
-@@ -3864,41 +3248,44 @@
+@@ -3864,41 +3240,44 @@
  								   false);
  						}
  					}
@@ -3534,7 +3459,7 @@
  				if (defaulttype < 0 && !isdst)
  					defaulttype = type;
  				addtt(starttime, type);
-@@ -3921,50 +3308,17 @@
+@@ -3921,50 +3300,17 @@
  	}
  	if (defaulttype < 0)
  		defaulttype = 0;
@@ -3592,7 +3517,7 @@
  		 * transitions up to that point.
  		 */
  		struct rule xr;
-@@ -4055,11 +3409,6 @@
+@@ -4055,11 +3401,6 @@
  		error(_("too many leap seconds"));
  		exit(EXIT_FAILURE);
  	}
@@ -3604,7 +3529,7 @@
  	for (i = 0; i < leapcnt; ++i)
  		if (t <= trans[i])
  			break;
-@@ -4094,6 +3443,13 @@
+@@ -4094,6 +3435,13 @@
  		last = corr[i] += last;
  	}
  
@@ -3618,7 +3543,7 @@
  	if (0 <= leapexpires)
  	{
  		leapexpires = oadd(leapexpires, last);
-@@ -4102,6 +3458,8 @@
+@@ -4102,6 +3450,8 @@
  			error(_("last Leap time does not precede Expires time"));
  			exit(EXIT_FAILURE);
  		}
@@ -3627,7 +3552,7 @@
  	}
  }
  
-@@ -4252,7 +3610,7 @@
+@@ -4252,7 +3602,7 @@
  }
  
  /* case-insensitive equality */
@@ -3636,7 +3561,7 @@
  ciequal(const char *ap, const char *bp)
  {
  	while (lowerit(*ap) == lowerit(*bp++))
-@@ -4261,7 +3619,7 @@
+@@ -4261,7 +3611,7 @@
  	return false;
  }
  
@@ -3645,7 +3570,7 @@
  itsabbr(const char *abbr, const char *word)
  {
  	if (lowerit(*abbr) != lowerit(*word))
-@@ -4278,7 +3636,7 @@
+@@ -4278,7 +3628,7 @@
  
  /* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case.  */
  
@@ -3654,7 +3579,7 @@
  ciprefix(char const *abbr, char const *word)
  {
  	do
-@@ -4355,22 +3713,24 @@
+@@ -4355,22 +3705,24 @@
  	return foundlp;
  }
  
@@ -3684,7 +3609,7 @@
  		do
  		{
  			if ((*dp = *cp++) != '"')
-@@ -4388,53 +3748,48 @@
+@@ -4388,53 +3740,48 @@
  		if (is_space(*cp))
  			++cp;
  		*dp = '\0';
@@ -3765,7 +3690,7 @@
  }
  
  /*
-@@ -4450,31 +3805,39 @@
+@@ -4450,31 +3797,39 @@
  	zic_t		dayoff;			/* with a nod to Margaret O. */
  	zic_t		t,
  				y;
@@ -3820,7 +3745,7 @@
  	}
  	while (m != rp->r_month)
  	{
-@@ -4497,17 +3860,27 @@
+@@ -4497,17 +3852,27 @@
  	dayoff = oadd(dayoff, i);
  	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ)
  	{
@@ -3852,7 +3777,7 @@
  					wday = 0;
  				++i;
  			}
-@@ -4515,7 +3888,7 @@
+@@ -4515,7 +3880,7 @@
  			{
  				dayoff = oadd(dayoff, -1);
  				if (--wday < 0)
@@ -3861,7 +3786,7 @@
  				--i;
  			}
  		if (i < 0 || i >= len_months[isleap(y)][m])
-@@ -4570,13 +3943,15 @@
+@@ -4570,13 +3935,15 @@
  /* Ensure that the directories of ARGNAME exist, by making any missing
     ones.  If ANCESTORS, do this only for ARGNAME's ancestors; otherwise,
     do it for ARGNAME too.  Exit with failure if there is trouble.
@@ -3880,7 +3805,7 @@
  	/*
  	 * On MS-Windows systems, do not worry about drive letters or backslashes,
  	 * as this should suffice in practice.  Time zone names do not use drive
-@@ -4603,20 +3978,15 @@
+@@ -4603,20 +3970,15 @@
  		if (mkdir(name, MKDIR_UMASK) != 0)
  		{
  			/*
@@ -3906,7 +3831,7 @@
  					  progname, name, strerror(err));
  				exit(EXIT_FAILURE);
  			}
-@@ -4626,3 +3996,20 @@
+@@ -4626,3 +3988,20 @@
  	}
  	free(name);
  }
--- localtime.c-master-vs-upstream.diff	2024-07-04 14:38:31.386232000 +1200
+++ localtime.c-patched-vs-upstream.diff	2024-07-04 14:37:46.783150000 +1200
@@ -1,6 +1,6 @@
 --- localtime.filtered.c	2024-07-04 13:04:23.744118000 +1200
-+++ localtime.c	2024-07-04 14:38:19.086065000 +1200
-@@ -3,64 +3,28 @@
++++ localtime.c	2024-07-04 14:17:36.438049000 +1200
+@@ -3,64 +3,29 @@
  /*
   * This file is in the public domain, so clarified as of
   * 1996-06-05 by Arthur David Olson.
@@ -25,6 +25,7 @@
 -#include "tzdir.h"
 -#include "tzfile.h"
  #include <fcntl.h>
++#include <stdint.h>
  
 -#if defined THREAD_SAFE && THREAD_SAFE
 -#include <pthread.h>
@@ -75,7 +76,7 @@
  #ifndef WILDABBR
  /*
   * Someone might make incorrect use of a time zone abbreviation:
-@@ -72,7 +36,7 @@
+@@ -72,7 +37,7 @@
   *		in which Daylight Saving Time is never observed.
   *	4.	They might reference tzname[0] after setting to a time zone
   *		in which Standard Time is never observed.
@@ -84,7 +85,7 @@
   * What's best to do in the above cases is open to debate;
   * for now, we just set things up so that in any of the five cases
   * WILDABBR is used. Another possibility: initialize tzname[0] to the
-@@ -81,89 +45,28 @@
+@@ -81,89 +46,28 @@
   * manual page of what this "time zone abbreviation" means (doing this so
   * that tzname[0] has the "normal" length of three characters).
   */
@@ -179,12 +180,8 @@
  };
  
  struct rule
-@@ -172,70 +75,37 @@
- 	int			r_day;			/* day number of rule */
- 	int			r_week;			/* week number of rule */
- 	int			r_mon;			/* month number of rule */
--	int_fast32_t r_time;		/* transition time of rule */
-+	int32		r_time;			/* transition time of rule */
+@@ -175,64 +79,31 @@
+ 	int_fast32_t r_time;		/* transition time of rule */
  };
  
 -static struct pg_tm *gmtsub(struct state const *, pg_time_t const *, int_fast32_t,
@@ -204,13 +201,13 @@
 -static struct state *lclptr;
 -static struct state *gmtptr;
 -#endif							/* defined ALL_STATE */
-+static struct pg_tm *gmtsub(pg_time_t const *timep, int32 offset,
++static struct pg_tm *gmtsub(pg_time_t const *timep, int_fast32_t offset,
 +							struct pg_tm *tmp);
 +static bool increment_overflow(int *ip, int j);
-+static bool increment_overflow_time(pg_time_t *tp, int32 j);
++static bool increment_overflow_time(pg_time_t *tp, int_fast32_t j);
 +static int64 leapcorr(struct state const *sp, pg_time_t t);
 +static struct pg_tm *timesub(pg_time_t const *timep,
-+							 int32 offset, struct state const *sp,
++							 int_fast32_t offset, struct state const *sp,
 +							 struct pg_tm *tmp);
 +static bool typesequiv(struct state const *sp, int a, int b);
  
@@ -259,12 +256,8 @@
 -
  /* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX.  */
  static void
--init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx)
-+init_ttinfo(struct ttinfo *s, int32 utoff, bool isdst, int desigidx)
- {
- 	s->tt_utoff = utoff;
- 	s->tt_isdst = isdst;
-@@ -244,25 +114,15 @@
+ init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx)
+@@ -244,16 +115,6 @@
  	s->tt_ttisut = false;
  }
  
@@ -278,64 +271,13 @@
 -	return memcmp(abbr, UNSPEC, sizeof UNSPEC) == 0;
 -}
 -
--static int_fast32_t
-+static int32
+ static int_fast32_t
  detzcode(const char *const codep)
  {
--	int_fast32_t result;
-+	int32		result;
- 	int			i;
--	int_fast32_t one = 1;
--	int_fast32_t halfmaxval = one << (32 - 2);
--	int_fast32_t maxval = halfmaxval - 1 + halfmaxval;
--	int_fast32_t minval = -1 - maxval;
-+	int32		one = 1;
-+	int32		halfmaxval = one << (32 - 2);
-+	int32		maxval = halfmaxval - 1 + halfmaxval;
-+	int32		minval = -1 - maxval;
- 
- 	result = codep[0] & 0x7f;
- 	for (i = 1; i < 4; ++i)
-@@ -274,21 +134,21 @@
- 		 * Do two's-complement negation even on non-two's-complement machines.
- 		 * If the result would be minval - 1, return minval.
- 		 */
--		result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0;
-+		result -= !TWOS_COMPLEMENT(int32) && result != 0;
- 		result += minval;
- 	}
+@@ -306,125 +167,25 @@
  	return result;
  }
  
--static int_fast64_t
-+static int64
- detzcode64(const char *const codep)
- {
--	int_fast64_t result;
-+	uint64		result;
- 	int			i;
--	int_fast64_t one = 1;
--	int_fast64_t halfmaxval = one << (64 - 2);
--	int_fast64_t maxval = halfmaxval - 1 + halfmaxval;
--	int_fast64_t minval = -TWOS_COMPLEMENT(int_fast64_t) - maxval;
-+	int64		one = 1;
-+	int64		halfmaxval = one << (64 - 2);
-+	int64		maxval = halfmaxval - 1 + halfmaxval;
-+	int64		minval = -TWOS_COMPLEMENT(int64) - maxval;
- 
- 	result = codep[0] & 0x7f;
- 	for (i = 1; i < 8; ++i)
-@@ -300,131 +160,31 @@
- 		 * Do two's-complement negation even on non-two's-complement machines.
- 		 * If the result would be minval - 1, return minval.
- 		 */
--		result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0;
-+		result -= !TWOS_COMPLEMENT(int64) && result != 0;
- 		result += minval;
- 	}
- 	return result;
- }
- 
 -static void
 -update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp)
 +static bool
@@ -464,7 +406,7 @@
  /* Local storage needed for 'tzloadbody'.  */
  union local_storage
  {
-@@ -438,28 +198,23 @@
+@@ -438,28 +199,23 @@
  		struct state st;
  	}			u;
  
@@ -500,7 +442,7 @@
  	union input_buffer *up = &lsp->u.u;
  	int			tzheadsize = sizeof(struct tzhead);
  
-@@ -474,50 +229,10 @@
+@@ -474,50 +230,10 @@
  
  	if (name[0] == ':')
  		++name;
@@ -553,33 +495,23 @@
  
  	nread = read(fid, up->buf, sizeof up->buf);
  	if (nread < tzheadsize)
-@@ -531,17 +246,14 @@
+@@ -531,13 +247,10 @@
  		return errno;
  	for (stored = 4; stored <= 8; stored *= 2)
  	{
 -		char		version = up->tzhead.tzh_version[0];
 -		bool		skip_datablock = stored == 4 && version;
 -		int_fast32_t datablock_size;
--		int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
--		int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
+ 		int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
+ 		int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
 -		int_fast64_t prevtr = -1;
 -		int_fast32_t prevcorr;
--		int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt);
--		int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt);
--		int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt);
--		int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt);
-+		int32		ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
-+		int32		ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
-+		int64		prevtr = 0;
-+		int32		prevcorr = 0;
-+		int32		leapcnt = detzcode(up->tzhead.tzh_leapcnt);
-+		int32		timecnt = detzcode(up->tzhead.tzh_timecnt);
-+		int32		typecnt = detzcode(up->tzhead.tzh_typecnt);
-+		int32		charcnt = detzcode(up->tzhead.tzh_charcnt);
- 		char const *p = up->buf + tzheadsize;
- 
- 		/*
-@@ -553,177 +265,152 @@
++		int_fast64_t prevtr = 0;
++		int_fast32_t prevcorr = 0;
+ 		int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt);
+ 		int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt);
+ 		int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt);
+@@ -553,177 +266,152 @@
  			  && 0 <= typecnt && typecnt < TZ_MAX_TYPES
  			  && 0 <= timecnt && timecnt < TZ_MAX_TIMES
  			  && 0 <= charcnt && charcnt < TZ_MAX_CHARS
@@ -743,8 +675,8 @@
 +		leapcnt = 0;
 +		for (i = 0; i < sp->leapcnt; ++i)
 +		{
-+			int64		tr = stored == 4 ? detzcode(p) : detzcode64(p);
-+			int32		corr = detzcode(p + stored);
++			int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p);
++			int_fast32_t corr = detzcode(p + stored);
  
 -			/* Read leap seconds, discarding those out of pg_time_t range.  */
 -			leapcnt = 0;
@@ -764,7 +696,7 @@
 -				 * Leap seconds cannot occur before the Epoch, or out of
 -				 * order.
 +				 * Leap seconds cannot occur more than once per UTC month, and
-+				 * UTC months are at least 28 days long (minus 1 second for a
++				 * UTC months are at least 2 days long (minus 1 second for a
 +				 * negative leap second).  Each leap second's correction must
 +				 * differ from the previous one's by 1 second.
  				 */
@@ -870,7 +802,7 @@
  	}
  	if (doextend && nread > 2 &&
  		up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
-@@ -732,9 +419,8 @@
+@@ -732,9 +420,8 @@
  		struct state *ts = &lsp->u.st;
  
  		up->buf[nread - 1] = '\0';
@@ -881,7 +813,7 @@
  			/*
  			 * Attempt to reuse existing abbreviations. Without this,
  			 * America/Anchorage would be right on the edge after 2037 when
-@@ -785,25 +471,20 @@
+@@ -785,25 +472,20 @@
  						   == sp->types[sp->timecnt - 2]))
  					sp->timecnt--;
  
@@ -916,7 +848,7 @@
  				}
  				for (i = 0; i < ts->typecnt; i++)
  					sp->ttis[sp->typecnt++] = ts->ttis[i];
-@@ -812,6 +493,25 @@
+@@ -812,6 +494,25 @@
  	}
  	if (sp->typecnt == 0)
  		return EINVAL;
@@ -942,7 +874,7 @@
  
  	/*
  	 * Infer sp->defaulttype from the data.  Although this default type is
-@@ -826,13 +526,13 @@
+@@ -826,13 +527,13 @@
  	 */
  
  	/*
@@ -959,7 +891,7 @@
  
  	/*
  	 * Absent the above, if there are transition times and the first
-@@ -878,29 +578,49 @@
+@@ -878,29 +579,49 @@
  }
  
  /* Load tz data from the file named NAME into *SP.  Read extended
@@ -1021,7 +953,7 @@
  }
  
  static const int mon_lengths[2][MONSPERYEAR] = {
-@@ -912,20 +632,13 @@
+@@ -912,20 +633,13 @@
  	DAYSPERNYEAR, DAYSPERLYEAR
  };
  
@@ -1043,7 +975,7 @@
  getzname(const char *strp)
  {
  	char		c;
-@@ -946,7 +659,7 @@
+@@ -946,7 +660,7 @@
   * We don't do any checking here; checking is done later in common-case code.
   */
  
@@ -1052,16 +984,9 @@
  getqzname(const char *strp, const int delim)
  {
  	int			c;
-@@ -994,20 +707,19 @@
-  */
+@@ -1000,8 +714,8 @@
+ 	int_fast32_t secsperhour = SECSPERHOUR;
  
- static const char *
--getsecs(const char *strp, int_fast32_t * const secsp)
-+getsecs(const char *strp, int32 *const secsp)
- {
- 	int			num;
--	int_fast32_t secsperhour = SECSPERHOUR;
- 
  	/*
 -	 * 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-POSIX rules like
 -	 * "M10.4.6/26", which does not conform to POSIX, but which specifies the
@@ -1070,24 +995,8 @@
  	 * equivalent of "02:00 on the first Sunday on or after 23 Oct".
  	 */
  	strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
- 	if (strp == NULL)
- 		return NULL;
--	*secsp = num * secsperhour;
-+	*secsp = num * (int32) SECSPERHOUR;
- 	if (*strp == ':')
- 	{
- 		++strp;
-@@ -1036,7 +748,7 @@
-  */
+@@ -1057,8 +771,7 @@
  
- static const char *
--getoffset(const char *strp, int_fast32_t * const offsetp)
-+getoffset(const char *strp, int32 *const offsetp)
- {
- 	bool		neg = false;
- 
-@@ -1057,8 +769,7 @@
- 
  /*
   * Given a pointer into a timezone string, extract a rule in the form
 - * date[/time]. See POSIX Base Definitions section 8.3 variable TZ
@@ -1096,23 +1005,7 @@
   * If a valid rule is not found, return NULL.
   * Otherwise, return a pointer to the first character not part of the rule.
   */
-@@ -1124,12 +835,12 @@
-  * effect, calculate the year-relative time that rule takes effect.
-  */
- 
--static int_fast32_t
-+static int32
- transtime(const int year, const struct rule *const rulep,
--		  const int_fast32_t offset)
-+		  const int32 offset)
- {
- 	bool		leapyear;
--	int_fast32_t value;
-+	int32		value;
- 	int			i;
- 	int			d,
- 				m1,
-@@ -1138,6 +849,7 @@
+@@ -1138,6 +851,7 @@
  				yy2,
  				dow;
  
@@ -1120,7 +1013,7 @@
  	leapyear = isleap(year);
  	switch (rulep->r_type)
  	{
-@@ -1193,7 +905,7 @@
+@@ -1193,7 +907,7 @@
  			for (i = 1; i < rulep->r_week; ++i)
  			{
  				if (d + DAYSPERWEEK >=
@@ -1129,7 +1022,7 @@
  					break;
  				d += DAYSPERWEEK;
  			}
-@@ -1203,11 +915,8 @@
+@@ -1203,11 +917,8 @@
  			 */
  			value = d * SECSPERDAY;
  			for (i = 0; i < rulep->r_mon - 1; ++i)
@@ -1142,7 +1035,7 @@
  	}
  
  	/*
-@@ -1219,64 +928,71 @@
+@@ -1219,64 +930,71 @@
  }
  
  /*
@@ -1159,14 +1052,12 @@
  {
  	const char *stdname;
 -	const char *dstname;
--	int_fast32_t stdoffset;
--	int_fast32_t dstoffset;
 +	const char *dstname = NULL;
 +	size_t		stdlen;
 +	size_t		dstlen;
 +	size_t		charcnt;
-+	int32		stdoffset;
-+	int32		dstoffset;
+ 	int_fast32_t stdoffset;
+ 	int_fast32_t dstoffset;
  	char	   *cp;
  	bool		load_ok;
 -	ptrdiff_t	stdlen,
@@ -1258,7 +1149,7 @@
  	if (*name != '\0')
  	{
  		if (*name == '<')
-@@ -1294,9 +1010,11 @@
+@@ -1294,9 +1012,11 @@
  			name = getzname(name);
  			dstlen = name - dstname;	/* length of DST abbr. */
  		}
@@ -1271,22 +1162,21 @@
  		if (*name != '\0' && *name != ',' && *name != ';')
  		{
  			name = getoffset(name, &dstoffset);
-@@ -1312,11 +1030,11 @@
+@@ -1312,11 +1032,11 @@
  			struct rule start;
  			struct rule end;
  			int			year;
 +			int			yearlim;
  			int			timecnt;
  			pg_time_t	janfirst;
--			int_fast32_t janoffset = 0;
+ 			int_fast32_t janoffset = 0;
 -			int			yearbeg,
 -						yearlim;
-+			int32		janoffset = 0;
 +			int			yearbeg;
  
  			++name;
  			if ((name = getrule(name, &start)) == NULL)
-@@ -1334,13 +1052,14 @@
+@@ -1334,6 +1054,7 @@
  			 */
  			init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
  			init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
@@ -1294,15 +1184,7 @@
  			timecnt = 0;
  			janfirst = 0;
  			yearbeg = EPOCH_YEAR;
- 
- 			do
- 			{
--				int_fast32_t yearsecs
-+				int32		yearsecs
- 				= year_lengths[isleap(yearbeg - 1)] * SECSPERDAY;
- 
- 				yearbeg--;
-@@ -1349,72 +1068,48 @@
+@@ -1349,27 +1070,9 @@
  					janoffset = -yearsecs;
  					break;
  				}
@@ -1331,27 +1213,8 @@
 +			yearlim = yearbeg + YEARSPERREPEAT + 1;
  			for (year = yearbeg; year < yearlim; year++)
  			{
--				int_fast32_t
--					starttime = transtime(year, &start, stdoffset),
--					endtime = transtime(year, &end, dstoffset);
--				int_fast32_t
--					yearsecs = (year_lengths[isleap(year)]
--								* SECSPERDAY);
-+				int32
-+							starttime = transtime(year, &start, stdoffset),
-+							endtime = transtime(year, &end, dstoffset);
-+				int32
-+							yearsecs = (year_lengths[isleap(year)]
-+										* SECSPERDAY);
- 				bool		reversed = endtime < starttime;
- 
- 				if (reversed)
- 				{
--					int_fast32_t swap = starttime;
-+					int32		swap = starttime;
- 
- 					starttime = endtime;
- 					endtime = swap;
+ 				int_fast32_t
+@@ -1389,32 +1092,26 @@
  				}
  				if (reversed
  					|| (starttime < endtime
@@ -1390,7 +1253,7 @@
  				if (increment_overflow_time
  					(&janfirst, janoffset + yearsecs))
  					break;
-@@ -1426,14 +1121,14 @@
+@@ -1426,7 +1123,7 @@
  				sp->ttis[0] = sp->ttis[1];
  				sp->typecnt = 1;	/* Perpetual DST.  */
  			}
@@ -1399,17 +1262,7 @@
  				sp->goback = sp->goahead = true;
  		}
  		else
- 		{
--			int_fast32_t theirstdoffset;
--			int_fast32_t theirdstoffset;
--			int_fast32_t theiroffset;
-+			int32		theirstdoffset;
-+			int32		theirdstoffset;
-+			int32		theiroffset;
- 			bool		isdst;
- 			int			i;
- 			int			j;
-@@ -1471,6 +1166,7 @@
+@@ -1471,6 +1168,7 @@
  			 * Initially we're assumed to be in standard time.
  			 */
  			isdst = false;
@@ -1417,7 +1270,7 @@
  
  			/*
  			 * Now juggle transition times and types tracking offsets as you
-@@ -1495,7 +1191,7 @@
+@@ -1495,7 +1193,7 @@
  					 */
  					/*
  					 * Transitions from DST to DDST will effectively disappear
@@ -1426,7 +1279,7 @@
  					 */
  					if (isdst && !sp->ttis[j].tt_ttisstd)
  					{
-@@ -1521,6 +1217,7 @@
+@@ -1521,6 +1219,7 @@
  			init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
  			init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
  			sp->typecnt = 2;
@@ -1434,7 +1287,7 @@
  		}
  	}
  	else
-@@ -1529,8 +1226,8 @@
+@@ -1529,8 +1228,8 @@
  		sp->typecnt = 1;		/* only standard time */
  		sp->timecnt = 0;
  		init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
@@ -1444,7 +1297,7 @@
  	sp->charcnt = charcnt;
  	cp = sp->chars;
  	memcpy(cp, stdname, stdlen);
-@@ -1547,155 +1244,19 @@
+@@ -1547,155 +1246,19 @@
  static void
  gmtload(struct state *const sp)
  {
@@ -1603,7 +1456,7 @@
  		 struct pg_tm *const tmp)
  {
  	const struct ttinfo *ttisp;
-@@ -1704,14 +1265,11 @@
+@@ -1704,14 +1267,11 @@
  	const pg_time_t t = *timep;
  
  	if (sp == NULL)
@@ -1620,7 +1473,7 @@
  		pg_time_t	seconds;
  		pg_time_t	years;
  
-@@ -1720,34 +1278,19 @@
+@@ -1720,33 +1280,18 @@
  		else
  			seconds = t - sp->ats[sp->timecnt - 1];
  		--seconds;
@@ -1655,12 +1508,10 @@
 -						  result->tm_year, years))
 -				return NULL;
 -#else
--			int_fast64_t newy;
-+			int64		newy;
+ 			int_fast64_t newy;
  
  			newy = result->tm_year;
- 			if (t < sp->ats[0])
-@@ -1757,7 +1300,6 @@
+@@ -1757,7 +1302,6 @@
  			if (!(INT_MIN <= newy && newy <= INT_MAX))
  				return NULL;
  			result->tm_year = newy;
@@ -1668,7 +1519,7 @@
  		}
  		return result;
  	}
-@@ -1779,7 +1321,7 @@
+@@ -1779,7 +1323,7 @@
  			else
  				lo = mid + 1;
  		}
@@ -1677,7 +1528,7 @@
  	}
  	ttisp = &sp->ttis[i];
  
-@@ -1792,132 +1334,76 @@
+@@ -1792,132 +1336,76 @@
  	if (result)
  	{
  		result->tm_isdst = ttisp->tt_isdst;
@@ -1745,7 +1596,7 @@
  static struct pg_tm *
 -gmtsub(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, pg_time_t const *timep,
 -	   int_fast32_t offset, struct pg_tm *tmp)
-+gmtsub(pg_time_t const *timep, int32 offset,
++gmtsub(pg_time_t const *timep, int_fast32_t offset,
 +	   struct pg_tm *tmp)
  {
  	struct pg_tm *result;
@@ -1840,13 +1691,7 @@
  {
  	return (y < 0
  			? -1 - leaps_thru_end_of_nonneg(-1 - y)
-@@ -1925,28 +1411,21 @@
- }
- 
- static struct pg_tm *
--timesub(const pg_time_t *timep, int_fast32_t offset,
-+timesub(const pg_time_t *timep, int32 offset,
- 		const struct state *sp, struct pg_tm *tmp)
+@@ -1930,23 +1418,16 @@
  {
  	const struct lsinfo *lp;
  	pg_time_t	tdays;
@@ -1876,7 +1721,7 @@
  	i = (sp == NULL) ? 0 : sp->leapcnt;
  	while (--i >= 0)
  	{
-@@ -1954,175 +1433,134 @@
+@@ -1954,175 +1435,134 @@
  		if (*timep >= lp->ls_trans)
  		{
  			corr = lp->ls_corr;
@@ -2092,8 +1937,7 @@
  
  static bool
 -increment_overflow32(int_fast32_t * const lp, int const m)
-+increment_overflow_time(pg_time_t *tp, int32 j)
- {
+-{
 -#ifdef ckd_add
 -	return ckd_add(lp, *lp, m);
 -#else
@@ -2107,8 +1951,8 @@
 -}
 -
 -static bool
--increment_overflow_time(pg_time_t *tp, int_fast32_t j)
--{
+ increment_overflow_time(pg_time_t *tp, int_fast32_t j)
+ {
 -#ifdef ckd_add
 -	return ckd_add(tp, *tp, j);
 -#else
@@ -2124,7 +1968,7 @@
  	 */
  	if (!(j < 0
  		  ? (TYPE_SIGNED(pg_time_t) ? TIME_T_MIN - j <= *tp : -1 - j < *tp)
-@@ -2130,637 +1568,339 @@
+@@ -2130,637 +1570,339 @@
  		return true;
  	*tp += j;
  	return false;
From eb323ba495a302540945bcb9c6527bdcce87d9a2 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email-masked]>
Date: Thu, 18 Apr 2024 07:53:10 +1200
Subject: [PATCH v3 1/3] Use int64_t support from <stdint.h>.

Remove most of the configure probes for 64 bit integers.  Map our names
to the standard C99 names for types like int64_t, limits like INT64_MAX,
constant constructors like INT64_C().

Unfortunately we can't use standard printf format macros like PRId64
from <inttypes.h>, because we have our own snprintf.c implementation.
It does not understand Windows' PRId64 macro, for example.  Selecting
between "l" and "ll" by size alone is trivial, but we still some
configure-time help to work out which one will compile warning-free when
both are candidates, on systems that support printf format string type
checking.

We can get rid of pg_config_ext.h, which existed only to define
PG_INT64_TYPE for client interfaces, which is now redundant.

Reviewed-by: Peter Eisentraut <[email-masked]>
Reviewed-by: Heikki Linnakangas <[email-masked]>
Reviewed-by: Japin Li <[email-masked]>
Reviewed-by: Tom Lane <[email-masked]> (the idea, not the patch)
Discussion: https://postgr.es/m/ME3P282MB3166F9D1F71F787929C0C7E7B6312%40ME3P282MB3166.AUSP282.PROD.OUTLOOK.COM
---
 config/c-compiler.m4                          | 111 +++---
 configure                                     | 377 ++++++------------
 configure.ac                                  |  54 +--
 meson.build                                   |  63 +--
 src/Makefile.global.in                        |   6 -
 src/include/.gitignore                        |   2 -
 src/include/Makefile                          |   8 +-
 src/include/c.h                               |  78 ++--
 src/include/meson.build                       |  10 -
 src/include/pg_config.h.in                    |  17 +-
 src/include/pg_config_ext.h.in                |   7 -
 src/include/pg_config_ext.h.meson             |   7 -
 src/include/port/pg_bitutils.h                |  18 +-
 src/include/postgres_ext.h                    |   7 +-
 src/interfaces/ecpg/ecpglib/typename.c        |   5 +-
 src/interfaces/ecpg/include/ecpg_config.h.in  |   9 -
 src/interfaces/ecpg/include/meson.build       |   3 -
 .../ecpg/include/pgtypes_interval.h           |  13 +-
 src/interfaces/ecpg/include/sqltypes.h        |   2 +-
 .../test/expected/compat_informix-sqlda.c     |   2 +-
 src/port/pg_bitutils.c                        |   7 +-
 src/port/snprintf.c                           |  14 +-
 22 files changed, 268 insertions(+), 552 deletions(-)
 delete mode 100644 src/include/pg_config_ext.h.in
 delete mode 100644 src/include/pg_config_ext.h.meson

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 10f8c7bd0a9..b3fdf1b8c48 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -38,58 +38,41 @@ ac_c_werror_flag=$ac_save_c_werror_flag
 ])# PGAC_TEST_PRINTF_ARCHETYPE

-# PGAC_TYPE_64BIT_INT(TYPE)
-# -------------------------
-# Check if TYPE is a working 64 bit integer type. Set HAVE_TYPE_64 to
-# yes or no respectively, and define HAVE_TYPE_64 if yes.
-AC_DEFUN([PGAC_TYPE_64BIT_INT],
-[define([Ac_define], [translit([have_$1_64], [a-z *], [A-Z_P])])dnl
-define([Ac_cachevar], [translit([pgac_cv_type_$1_64], [ *], [_p])])dnl
-AC_CACHE_CHECK([whether $1 is 64 bits], [Ac_cachevar],
-[AC_RUN_IFELSE([AC_LANG_SOURCE(
-[typedef $1 ac_int64;
-
-/*
- * These are globals to discourage the compiler from folding all the
- * arithmetic tests down to compile-time constants.
- */
-ac_int64 a = 20000001;
-ac_int64 b = 40000005;
-
-int does_int64_work()
-{
-  ac_int64 c,d;
-
-  if (sizeof(ac_int64) != 8)
-    return 0;			/* definitely not the right size */
-
-  /* Do perfunctory checks to see if 64-bit arithmetic seems to work */
-  c = a * b;
-  d = (c + b) / b;
-  if (d != a+1)
-    return 0;
-  return 1;
-}
-
-int
-main() {
-  return (! does_int64_work());
-}])],
-[Ac_cachevar=yes],
-[Ac_cachevar=no],
-[# If cross-compiling, check the size reported by the compiler and
-# trust that the arithmetic works.
-AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([], [sizeof($1) == 8])],
-                  Ac_cachevar=yes,
-                  Ac_cachevar=no)])])
-
-Ac_define=$Ac_cachevar
-if test x"$Ac_cachevar" = xyes ; then
-  AC_DEFINE(Ac_define, 1, [Define to 1 if `]$1[' works and is 64 bits.])
+# PGAC_PRINTF_INT64_MODIFIER
+# --------------------------
+# Determine which printf format modifier to use for int64_t values, to avoid
+# warnings from the format string checker (if there is one).
+AC_DEFUN([PGAC_PRINTF_INT64_MODIFIER],
+[AC_CACHE_CHECK([for printf int64_t modifier], pgac_cv_int64_modifier,
+[
+ac_save_c_werror_flag=$ac_c_werror_flag
+ac_c_werror_flag=yes
+if test "$ac_cv_sizeof_long" -lt 8; then
+  # long isn't wide enough, so use "ll"
+  pgac_cv_int64_modifier='"ll"'
+else
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[#include <stdint.h>],
+[extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(printf,2,3)));
+ void
+ f()
+ {
+     emit_log(0, "%lld", (int64_t) 42);
+ }])],
+  # this system understands the attribute, and accepted "ll" without
+  # warning, so go with that (example: macOS)
+  [pgac_cv_int64_modifier='"ll"'],
+  # either this system doesn't understand the attribute at all (and
+  # "l" should be fine because now we only care about getting the
+  # right size), or it rejected "ll" with warnings because it expected
+  # "l" (example: FreeBSD)
+  [pgac_cv_int64_modifier='"l"'])
 fi
-undefine([Ac_define])dnl
-undefine([Ac_cachevar])dnl
-])# PGAC_TYPE_64BIT_INT
+ac_c_werror_flag=$ac_save_c_werror_flag
+])
+AC_DEFINE_UNQUOTED([INT64_MODIFIER], [$pgac_cv_int64_modifier],
+[Define to printf format modifier to use for int64_t.])
+])# PGAC_PRINTF_INT64_MODIFIER

 # PGAC_TYPE_128BIT_INT
@@ -269,10 +252,11 @@ fi])# PGAC_C_BUILTIN_CONSTANT_P
 # optimize away the call.
 AC_DEFUN([PGAC_C_BUILTIN_OP_OVERFLOW],
 [AC_CACHE_CHECK(for __builtin_mul_overflow, pgac_cv__builtin_op_overflow,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([
-PG_INT64_TYPE a = 1;
-PG_INT64_TYPE b = 1;
-PG_INT64_TYPE result;
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <stdint.h>],
+[
+int64_t a = 1;
+int64_t b = 1;
+int64_t result;
 int oflo;
 ],
 [oflo = __builtin_mul_overflow(a, b, &result);])],
@@ -557,9 +541,9 @@ fi])# PGAC_HAVE_GCC__SYNC_INT32_CAS
 # types, and define HAVE_GCC__SYNC_INT64_CAS if so.
 AC_DEFUN([PGAC_HAVE_GCC__SYNC_INT64_CAS],
 [AC_CACHE_CHECK(for builtin __sync int64 atomic operations, pgac_cv_gcc_sync_int64_cas,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
-  [PG_INT64_TYPE lock = 0;
-   __sync_val_compare_and_swap(&lock, 0, (PG_INT64_TYPE) 37);])],
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <stdint.h>],
+  [int64_t lock = 0;
+   __sync_val_compare_and_swap(&lock, 0, (int64_t) 37);])],
   [pgac_cv_gcc_sync_int64_cas="yes"],
   [pgac_cv_gcc_sync_int64_cas="no"])])
 if test x"$pgac_cv_gcc_sync_int64_cas" = x"yes"; then
@@ -588,9 +572,9 @@ fi])# PGAC_HAVE_GCC__ATOMIC_INT32_CAS
 # types, and define HAVE_GCC__ATOMIC_INT64_CAS if so.
 AC_DEFUN([PGAC_HAVE_GCC__ATOMIC_INT64_CAS],
 [AC_CACHE_CHECK(for builtin __atomic int64 atomic operations, pgac_cv_gcc_atomic_int64_cas,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
-  [PG_INT64_TYPE val = 0;
-   PG_INT64_TYPE expect = 0;
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <stdint.h>],
+  [int64_t val = 0;
+   int64_t expect = 0;
    __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);])],
   [pgac_cv_gcc_atomic_int64_cas="yes"],
   [pgac_cv_gcc_atomic_int64_cas="no"])])
@@ -733,9 +717,10 @@ AC_DEFUN([PGAC_AVX512_POPCNT_INTRINSICS],
 AC_CACHE_CHECK([for _mm512_popcnt_epi64 with CFLAGS=$1], [Ac_cachevar],
 [pgac_save_CFLAGS=$CFLAGS
 CFLAGS="$pgac_save_CFLAGS $1"
-AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <immintrin.h>],
+AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <immintrin.h>
+                                 #include <stdint.h>],
   [const char buf@<:@sizeof(__m512i)@:>@;
-   PG_INT64_TYPE popcnt = 0;
+   int64_t popcnt = 0;
    __m512i accum = _mm512_setzero_si512();
    const __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);
    const __m512i cnt = _mm512_popcnt_epi64(val);
diff --git a/configure b/configure
index 76f06bd8fda..cfec577a1c1 100755
--- a/configure
+++ b/configure
@@ -14546,6 +14546,42 @@ if test x"$pgac_cv__builtin_constant_p" = xyes ; then
 
 $as_echo "#define HAVE__BUILTIN_CONSTANT_P 1" >>confdefs.h
 
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_mul_overflow" >&5
+$as_echo_n "checking for __builtin_mul_overflow... " >&6; }
+if ${pgac_cv__builtin_op_overflow+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+int
+main ()
+{
+
+int64_t a = 1;
+int64_t b = 1;
+int64_t result;
+int oflo;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  pgac_cv__builtin_op_overflow=yes
+else
+  pgac_cv__builtin_op_overflow=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__builtin_op_overflow" >&5
+$as_echo "$pgac_cv__builtin_op_overflow" >&6; }
+if test x"$pgac_cv__builtin_op_overflow" = xyes ; then
+
+$as_echo "#define HAVE__BUILTIN_OP_OVERFLOW 1" >>confdefs.h
+
 fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_unreachable" >&5
 $as_echo_n "checking for __builtin_unreachable... " >&6; }
@@ -16439,236 +16475,6 @@ fi
 # Run tests below here
 # --------------------
 
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether long int is 64 bits" >&5
-$as_echo_n "checking whether long int is 64 bits... " >&6; }
-if ${pgac_cv_type_long_int_64+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if test "$cross_compiling" = yes; then :
-  # If cross-compiling, check the size reported by the compiler and
-# trust that the arithmetic works.
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-int
-main ()
-{
-static int test_array [1 - 2 * !(sizeof(long int) == 8)];
-test_array [0] = 0;
-return test_array [0];
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  pgac_cv_type_long_int_64=yes
-else
-  pgac_cv_type_long_int_64=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-typedef long int ac_int64;
-
-/*
- * These are globals to discourage the compiler from folding all the
- * arithmetic tests down to compile-time constants.
- */
-ac_int64 a = 20000001;
-ac_int64 b = 40000005;
-
-int does_int64_work()
-{
-  ac_int64 c,d;
-
-  if (sizeof(ac_int64) != 8)
-    return 0;			/* definitely not the right size */
-
-  /* Do perfunctory checks to see if 64-bit arithmetic seems to work */
-  c = a * b;
-  d = (c + b) / b;
-  if (d != a+1)
-    return 0;
-  return 1;
-}
-
-int
-main() {
-  return (! does_int64_work());
-}
-_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
-  pgac_cv_type_long_int_64=yes
-else
-  pgac_cv_type_long_int_64=no
-fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-  conftest.$ac_objext conftest.beam conftest.$ac_ext
-fi
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_type_long_int_64" >&5
-$as_echo "$pgac_cv_type_long_int_64" >&6; }
-
-HAVE_LONG_INT_64=$pgac_cv_type_long_int_64
-if test x"$pgac_cv_type_long_int_64" = xyes ; then
-
-$as_echo "#define HAVE_LONG_INT_64 1" >>confdefs.h
-
-fi
-
-
-if test x"$HAVE_LONG_INT_64" = x"yes" ; then
-  pg_int64_type="long int"
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether long long int is 64 bits" >&5
-$as_echo_n "checking whether long long int is 64 bits... " >&6; }
-if ${pgac_cv_type_long_long_int_64+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if test "$cross_compiling" = yes; then :
-  # If cross-compiling, check the size reported by the compiler and
-# trust that the arithmetic works.
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-int
-main ()
-{
-static int test_array [1 - 2 * !(sizeof(long long int) == 8)];
-test_array [0] = 0;
-return test_array [0];
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  pgac_cv_type_long_long_int_64=yes
-else
-  pgac_cv_type_long_long_int_64=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-typedef long long int ac_int64;
-
-/*
- * These are globals to discourage the compiler from folding all the
- * arithmetic tests down to compile-time constants.
- */
-ac_int64 a = 20000001;
-ac_int64 b = 40000005;
-
-int does_int64_work()
-{
-  ac_int64 c,d;
-
-  if (sizeof(ac_int64) != 8)
-    return 0;			/* definitely not the right size */
-
-  /* Do perfunctory checks to see if 64-bit arithmetic seems to work */
-  c = a * b;
-  d = (c + b) / b;
-  if (d != a+1)
-    return 0;
-  return 1;
-}
-
-int
-main() {
-  return (! does_int64_work());
-}
-_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
-  pgac_cv_type_long_long_int_64=yes
-else
-  pgac_cv_type_long_long_int_64=no
-fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-  conftest.$ac_objext conftest.beam conftest.$ac_ext
-fi
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_type_long_long_int_64" >&5
-$as_echo "$pgac_cv_type_long_long_int_64" >&6; }
-
-HAVE_LONG_LONG_INT_64=$pgac_cv_type_long_long_int_64
-if test x"$pgac_cv_type_long_long_int_64" = xyes ; then
-
-$as_echo "#define HAVE_LONG_LONG_INT_64 1" >>confdefs.h
-
-fi
-
-  if test x"$HAVE_LONG_LONG_INT_64" = x"yes" ; then
-    pg_int64_type="long long int"
-  else
-    as_fn_error $? "Cannot find a working 64-bit integer type." "$LINENO" 5
-  fi
-fi
-
-
-cat >>confdefs.h <<_ACEOF
-#define PG_INT64_TYPE $pg_int64_type
-_ACEOF
-
-
-# Select the printf length modifier that goes with that, too.
-if test x"$pg_int64_type" = x"long long int" ; then
-  INT64_MODIFIER='"ll"'
-else
-  INT64_MODIFIER='"l"'
-fi
-
-
-cat >>confdefs.h <<_ACEOF
-#define INT64_MODIFIER $INT64_MODIFIER
-_ACEOF
-
-
-# has to be down here, rather than with the other builtins, because
-# the test uses PG_INT64_TYPE.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_mul_overflow" >&5
-$as_echo_n "checking for __builtin_mul_overflow... " >&6; }
-if ${pgac_cv__builtin_op_overflow+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-PG_INT64_TYPE a = 1;
-PG_INT64_TYPE b = 1;
-PG_INT64_TYPE result;
-int oflo;
-
-int
-main ()
-{
-oflo = __builtin_mul_overflow(a, b, &result);
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  pgac_cv__builtin_op_overflow=yes
-else
-  pgac_cv__builtin_op_overflow=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__builtin_op_overflow" >&5
-$as_echo "$pgac_cv__builtin_op_overflow" >&6; }
-if test x"$pgac_cv__builtin_op_overflow" = xyes ; then
-
-$as_echo "#define HAVE__BUILTIN_OP_OVERFLOW 1" >>confdefs.h
-
-fi
-
 # Check size of void *, size_t (enables tweaks for > 32bit address space)
 # The cast to long int works around a bug in the HP C Compiler
 # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
@@ -16877,43 +16683,41 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF

-if test x"$HAVE_LONG_LONG_INT_64" = x"yes" ; then
-  # The cast to long int works around a bug in the HP C Compiler,
+# The cast to long int works around a bug in the HP C Compiler,
 # see AC_CHECK_SIZEOF for more information.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking alignment of long long int" >&5
-$as_echo_n "checking alignment of long long int... " >&6; }
-if ${ac_cv_alignof_long_long_int+:} false; then :
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking alignment of int64_t" >&5
+$as_echo_n "checking alignment of int64_t... " >&6; }
+if ${ac_cv_alignof_int64_t+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-  if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_long_long_int"        "$ac_includes_default
+  if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_int64_t"        "$ac_includes_default
 #ifndef offsetof
 # define offsetof(type, member) ((char *) &((type *) 0)->member - (char *) 0)
 #endif
-typedef struct { char x; long long int y; } ac__type_alignof_;"; then :
+typedef struct { char x; int64_t y; } ac__type_alignof_;"; then :
 
 else
-  if test "$ac_cv_type_long_long_int" = yes; then
+  if test "$ac_cv_type_int64_t" = yes; then
      { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error 77 "cannot compute alignment of long long int
+as_fn_error 77 "cannot compute alignment of int64_t
 See \`config.log' for more details" "$LINENO" 5; }
    else
-     ac_cv_alignof_long_long_int=0
+     ac_cv_alignof_int64_t=0
    fi
 fi
 
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_alignof_long_long_int" >&5
-$as_echo "$ac_cv_alignof_long_long_int" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_alignof_int64_t" >&5
+$as_echo "$ac_cv_alignof_int64_t" >&6; }

 cat >>confdefs.h <<_ACEOF
-#define ALIGNOF_LONG_LONG_INT $ac_cv_alignof_long_long_int
+#define ALIGNOF_INT64_T $ac_cv_alignof_int64_t
 _ACEOF

-fi
 # The cast to long int works around a bug in the HP C Compiler,
 # see AC_CHECK_SIZEOF for more information.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking alignment of double" >&5
@@ -16971,8 +16775,8 @@ MAX_ALIGNOF=$ac_cv_alignof_double
 if test $ac_cv_alignof_long -gt $MAX_ALIGNOF ; then
   as_fn_error $? "alignment of 'long' is greater than the alignment of 'double'" "$LINENO" 5
 fi
-if test x"$HAVE_LONG_LONG_INT_64" = xyes && test $ac_cv_alignof_long_long_int -gt $MAX_ALIGNOF ; then
-  as_fn_error $? "alignment of 'long long int' is greater than the alignment of 'double'" "$LINENO" 5
+if test $ac_cv_alignof_int64_t -gt $MAX_ALIGNOF ; then
+  as_fn_error $? "alignment of 'int64_t' is greater than the alignment of 'double'" "$LINENO" 5
 fi
 
 cat >>confdefs.h <<_ACEOF
@@ -16980,7 +16784,6 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF

-
 # Some platforms predefine the types int8, int16, etc.  Only check
 # a (hopefully) representative subset.
 ac_fn_c_check_type "$LINENO" "int8" "ac_cv_type_int8" "#include <stdio.h>
@@ -17025,6 +16828,60 @@ _ACEOF
 fi

+# Check how to format int64_t without warnings from printf.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for printf int64_t modifier" >&5
+$as_echo_n "checking for printf int64_t modifier... " >&6; }
+if ${pgac_cv_int64_modifier+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+ac_save_c_werror_flag=$ac_c_werror_flag
+ac_c_werror_flag=yes
+if test "$ac_cv_sizeof_long" -lt 8; then
+  # long isn't wide enough, so use "ll"
+  pgac_cv_int64_modifier='"ll"'
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+int
+main ()
+{
+extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(printf,2,3)));
+ void
+ f()
+ {
+     emit_log(0, "%lld", (int64_t) 42);
+ }
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  # this system understands the attribute, and accepted "ll" without
+  # warning, so go with that (example: macOS)
+  pgac_cv_int64_modifier='"ll"'
+else
+  # either this system doesn't understand the attribute at all (and
+  # "l" should be fine because now we only care about getting the
+  # right size), or it rejected "ll" with warnings because it expected
+  # "l" (example: FreeBSD)
+  pgac_cv_int64_modifier='"l"'
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_c_werror_flag=$ac_save_c_werror_flag
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_int64_modifier" >&5
+$as_echo "$pgac_cv_int64_modifier" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define INT64_MODIFIER $pgac_cv_int64_modifier
+_ACEOF
+
+
+
 # Some compilers offer a 128-bit integer scalar type.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __int128" >&5
 $as_echo_n "checking for __int128... " >&6; }
@@ -17279,12 +17136,12 @@ if ${pgac_cv_gcc_sync_int64_cas+:} false; then :
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-
+#include <stdint.h>
 int
 main ()
 {
-PG_INT64_TYPE lock = 0;
-   __sync_val_compare_and_swap(&lock, 0, (PG_INT64_TYPE) 37);
+int64_t lock = 0;
+   __sync_val_compare_and_swap(&lock, 0, (int64_t) 37);
   ;
   return 0;
 }
@@ -17344,12 +17201,12 @@ if ${pgac_cv_gcc_atomic_int64_cas+:} false; then :
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-
+#include <stdint.h>
 int
 main ()
 {
-PG_INT64_TYPE val = 0;
-   PG_INT64_TYPE expect = 0;
+int64_t val = 0;
+   int64_t expect = 0;
    __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);
   ;
   return 0;
@@ -17604,11 +17461,12 @@ CFLAGS="$pgac_save_CFLAGS "
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <immintrin.h>
+                                 #include <stdint.h>
 int
 main ()
 {
 const char buf[sizeof(__m512i)];
-   PG_INT64_TYPE popcnt = 0;
+   int64_t popcnt = 0;
    __m512i accum = _mm512_setzero_si512();
    const __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);
    const __m512i cnt = _mm512_popcnt_epi64(val);
@@ -17647,11 +17505,12 @@ CFLAGS="$pgac_save_CFLAGS -mavx512vpopcntdq -mavx512bw"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <immintrin.h>
+                                 #include <stdint.h>
 int
 main ()
 {
 const char buf[sizeof(__m512i)];
-   PG_INT64_TYPE popcnt = 0;
+   int64_t popcnt = 0;
    __m512i accum = _mm512_setzero_si512();
    const __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);
    const __m512i cnt = _mm512_popcnt_epi64(val);
@@ -19272,9 +19131,6 @@ fi
 ac_config_headers="$ac_config_headers src/include/pg_config.h"

-ac_config_headers="$ac_config_headers src/include/pg_config_ext.h"
-
-
 ac_config_headers="$ac_config_headers src/interfaces/ecpg/include/ecpg_config.h"

@@ -19989,7 +19845,6 @@ do
     "src/Makefile.port") CONFIG_LINKS="$CONFIG_LINKS src/Makefile.port:src/makefiles/Makefile.${template}" ;;
     "check_win32_symlinks") CONFIG_COMMANDS="$CONFIG_COMMANDS check_win32_symlinks" ;;
     "src/include/pg_config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/include/pg_config.h" ;;
-    "src/include/pg_config_ext.h") CONFIG_HEADERS="$CONFIG_HEADERS src/include/pg_config_ext.h" ;;
     "src/interfaces/ecpg/include/ecpg_config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/interfaces/ecpg/include/ecpg_config.h" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
@@ -20597,10 +20452,6 @@ $as_echo "$as_me: WARNING: *** link for $FILE -- please fix by hand" >&2;}
     "src/include/pg_config.h":H)
 # Update timestamp for pg_config.h (see Makefile.global)
 echo >src/include/stamp-h
- ;;
-    "src/include/pg_config_ext.h":H)
-# Update timestamp for pg_config_ext.h (see Makefile.global)
-echo >src/include/stamp-ext-h
  ;;
     "src/interfaces/ecpg/include/ecpg_config.h":H) echo >src/interfaces/ecpg/include/stamp-h ;;
 
diff --git a/configure.ac b/configure.ac
index ab2d51c21ce..10bfb994d4f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1630,6 +1630,7 @@ PGAC_C_STATIC_ASSERT
 PGAC_C_TYPEOF
 PGAC_C_TYPES_COMPATIBLE
 PGAC_C_BUILTIN_CONSTANT_P
+PGAC_C_BUILTIN_OP_OVERFLOW
 PGAC_C_BUILTIN_UNREACHABLE
 PGAC_C_COMPUTED_GOTO
 PGAC_STRUCT_TIMEZONE
@@ -1945,41 +1946,6 @@ for the exact reason.]])],
 # Run tests below here
 # --------------------
 
-dnl Check to see if we have a working 64-bit integer type.
-dnl Since Postgres 8.4, we no longer support compilers without a working
-dnl 64-bit type; but we have to determine whether that type is called
-dnl "long int" or "long long int".
-
-PGAC_TYPE_64BIT_INT([long int])
-
-if test x"$HAVE_LONG_INT_64" = x"yes" ; then
-  pg_int64_type="long int"
-else
-  PGAC_TYPE_64BIT_INT([long long int])
-  if test x"$HAVE_LONG_LONG_INT_64" = x"yes" ; then
-    pg_int64_type="long long int"
-  else
-    AC_MSG_ERROR([Cannot find a working 64-bit integer type.])
-  fi
-fi
-
-AC_DEFINE_UNQUOTED(PG_INT64_TYPE, $pg_int64_type,
-  [Define to the name of a signed 64-bit integer type.])
-
-# Select the printf length modifier that goes with that, too.
-if test x"$pg_int64_type" = x"long long int" ; then
-  INT64_MODIFIER='"ll"'
-else
-  INT64_MODIFIER='"l"'
-fi
-
-AC_DEFINE_UNQUOTED(INT64_MODIFIER, $INT64_MODIFIER,
-                   [Define to the appropriate printf length modifier for 64-bit ints.])
-
-# has to be down here, rather than with the other builtins, because
-# the test uses PG_INT64_TYPE.
-PGAC_C_BUILTIN_OP_OVERFLOW
-
 # Check size of void *, size_t (enables tweaks for > 32bit address space)
 AC_CHECK_SIZEOF([void *])
 AC_CHECK_SIZEOF([size_t])
@@ -1990,9 +1956,7 @@ AC_CHECK_SIZEOF([long])
 AC_CHECK_ALIGNOF(short)
 AC_CHECK_ALIGNOF(int)
 AC_CHECK_ALIGNOF(long)
-if test x"$HAVE_LONG_LONG_INT_64" = x"yes" ; then
-  AC_CHECK_ALIGNOF(long long int)
-fi
+AC_CHECK_ALIGNOF(int64_t)
 AC_CHECK_ALIGNOF(double)
 
 # Compute maximum alignment of any basic type.
@@ -2016,17 +1980,19 @@ MAX_ALIGNOF=$ac_cv_alignof_double
 if test $ac_cv_alignof_long -gt $MAX_ALIGNOF ; then
   AC_MSG_ERROR([alignment of 'long' is greater than the alignment of 'double'])
 fi
-if test x"$HAVE_LONG_LONG_INT_64" = xyes && test $ac_cv_alignof_long_long_int -gt $MAX_ALIGNOF ; then
-  AC_MSG_ERROR([alignment of 'long long int' is greater than the alignment of 'double'])
+if test $ac_cv_alignof_int64_t -gt $MAX_ALIGNOF ; then
+  AC_MSG_ERROR([alignment of 'int64_t' is greater than the alignment of 'double'])
 fi
 AC_DEFINE_UNQUOTED(MAXIMUM_ALIGNOF, $MAX_ALIGNOF, [Define as the maximum alignment requirement of any C data type.])
 
-
 # Some platforms predefine the types int8, int16, etc.  Only check
 # a (hopefully) representative subset.
 AC_CHECK_TYPES([int8, uint8, int64, uint64], [], [],
 [#include <stdio.h>])
 
+# Check how to format int64_t without warnings from printf.
+PGAC_PRINTF_INT64_MODIFIER
+
 # Some compilers offer a 128-bit integer scalar type.
 PGAC_TYPE_128BIT_INT
 
@@ -2520,12 +2486,6 @@ AC_CONFIG_HEADERS([src/include/pg_config.h],
 echo >src/include/stamp-h
 ])
 
-AC_CONFIG_HEADERS([src/include/pg_config_ext.h],
-[
-# Update timestamp for pg_config_ext.h (see Makefile.global)
-echo >src/include/stamp-ext-h
-])
-
 AC_CONFIG_HEADERS([src/interfaces/ecpg/include/ecpg_config.h],
                   [echo >src/interfaces/ecpg/include/stamp-h])
 
diff --git a/meson.build b/meson.build
index 5387bb6d5fd..6534bf6e17d 100644
--- a/meson.build
+++ b/meson.build
@@ -1491,18 +1491,6 @@ endif
 
 sizeof_long = cc.sizeof('long', args: test_c_args)
 cdata.set('SIZEOF_LONG', sizeof_long)
-if sizeof_long == 8
-  cdata.set('HAVE_LONG_INT_64', 1)
-  pg_int64_type = 'long int'
-  cdata.set_quoted('INT64_MODIFIER', 'l')
-elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
-  cdata.set('HAVE_LONG_LONG_INT_64', 1)
-  pg_int64_type = 'long long int'
-  cdata.set_quoted('INT64_MODIFIER', 'll')
-else
-  error('do not know how to get a 64bit int')
-endif
-cdata.set('PG_INT64_TYPE', pg_int64_type)
 
 if host_machine.endian() == 'big'
   cdata.set('WORDS_BIGENDIAN', 1)
@@ -1532,8 +1520,8 @@ endforeach
 # any types wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8
 # would be too much of a penalty for disk and memory space.
 alignof_double = cdata.get('ALIGNOF_DOUBLE')
-if cc.alignment(pg_int64_type, args: test_c_args) > alignof_double
-  error('alignment of int64 is greater than the alignment of double')
+if cc.alignment('int64_t', args: test_c_args, prefix: '#include <stdint.h>') > alignof_double
+  error('alignment of int64_t is greater than the alignment of double')
 endif
 cdata.set('MAXIMUM_ALIGNOF', alignof_double)
 
@@ -1686,6 +1674,34 @@ foreach a : printf_attributes
 endforeach

+# Determine which printf format modifier to use for int64_t values, to avoid
+# warnings from the format string checker (if there is one).
+if cdata.get('SIZEOF_LONG') < 8
+  # long isn't wide enough, so use "ll"
+  cdata.set('INT64_MODIFIER', '"ll"')
+else
+  testsrc = '''
+#include <stdint.h>
+extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(printf,2,3)));
+void
+f()
+{
+    emit_log(0, "%lld", (int64_t) 42);
+}
+'''
+  if cc.compiles(testsrc, args: test_c_args + attrib_error_args)
+	# this system understands the attribute, and accepted "ll" without warning,
+	# so go with that (example: macOS)
+    cdata.set('INT64_MODIFIER', '"ll"')
+  else
+	# either this system doesn't understand the attribute at all (and "l" should
+	# be fine because now we only care about getting the right size), or it
+	# rejected "ll" with warnings because it expected "l" (example: FreeBSD)
+    cdata.set('INT64_MODIFIER', '"l"')
+  endif
+endif
+
+
 if cc.has_function_attribute('visibility:default') and \
     cc.has_function_attribute('visibility:hidden')
   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
@@ -1745,17 +1761,17 @@ endif
 # compile, and store the results in global variables so the compiler doesn't
 # optimize away the call.
 if cc.links('''
-    INT64 a = 1;
-    INT64 b = 1;
-    INT64 result;
+    #include <stdint.h>
+    int64_t a = 1;
+    int64_t b = 1;
+    int64_t result;
 
     int main(void)
     {
         return __builtin_mul_overflow(a, b, &result);
     }''',
     name: '__builtin_mul_overflow',
-    args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
-    )
+    args: test_c_args)
   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
 endif
 
@@ -2048,7 +2064,7 @@ int main(void)
     cdata.set(check['name'],
       cc.links(test,
         name: check['desc'],
-        args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
+        args: test_c_args) ? 1 : false
     )
   endforeach
 
@@ -2092,11 +2108,12 @@ if host_cpu == 'x86_64'
 
   prog = '''
 #include <immintrin.h>
+#include <stdint.h>
 
 int main(void)
 {
     const char buf[sizeof(__m512i)];
-    INT64 popcnt = 0;
+    int64_t popcnt = 0;
     __m512i accum = _mm512_setzero_si512();
     const __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);
     const __m512i cnt = _mm512_popcnt_epi64(val);
@@ -2108,10 +2125,10 @@ int main(void)
 '''
 
   if cc.links(prog, name: 'AVX-512 popcount without -mavx512vpopcntdq -mavx512bw',
-        args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))])
+        args: test_c_args)
     cdata.set('USE_AVX512_POPCNT_WITH_RUNTIME_CHECK', 1)
   elif cc.links(prog, name: 'AVX-512 popcount with -mavx512vpopcntdq -mavx512bw',
-        args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))] + ['-mavx512vpopcntdq'] + ['-mavx512bw'])
+        args: test_c_args + ['-mavx512vpopcntdq'] + ['-mavx512bw'])
     cdata.set('USE_AVX512_POPCNT_WITH_RUNTIME_CHECK', 1)
     cflags_popcnt += ['-mavx512vpopcntdq'] + ['-mavx512bw']
   endif
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 83b91fe9167..5fa9f3d6748 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -834,12 +834,6 @@ $(top_builddir)/src/include/pg_config.h: $(top_builddir)/src/include/stamp-h ;
 $(top_builddir)/src/include/stamp-h: $(top_srcdir)/src/include/pg_config.h.in $(top_builddir)/config.status
 	cd $(top_builddir) && ./config.status src/include/pg_config.h
 
-# Also remake pg_config_ext.h from pg_config_ext.h.in, same logic as above.
-$(top_builddir)/src/include/pg_config_ext.h: $(top_builddir)/src/include/stamp-ext-h ;
-
-$(top_builddir)/src/include/stamp-ext-h: $(top_srcdir)/src/include/pg_config_ext.h.in $(top_builddir)/config.status
-	cd $(top_builddir) && ./config.status src/include/pg_config_ext.h
-
 # Also remake ecpg_config.h from ecpg_config.h.in if the latter changed, same
 # logic as above.
 $(top_builddir)/src/interfaces/ecpg/include/ecpg_config.h: $(top_builddir)/src/interfaces/ecpg/include/stamp-h ;
diff --git a/src/include/.gitignore b/src/include/.gitignore
index 51819fb4ddd..6e99e82fe0c 100644
--- a/src/include/.gitignore
+++ b/src/include/.gitignore
@@ -1,5 +1,3 @@
 /stamp-h
-/stamp-ext-h
 /pg_config.h
-/pg_config_ext.h
 /pg_config_os.h
diff --git a/src/include/Makefile b/src/include/Makefile
index b8b576a4de3..26332ff9cbc 100644
--- a/src/include/Makefile
+++ b/src/include/Makefile
@@ -13,7 +13,7 @@ top_builddir = ../..
 include $(top_builddir)/src/Makefile.global

-all: pg_config.h pg_config_ext.h pg_config_os.h
+all: pg_config.h pg_config_os.h

 # Subdirectories containing installable headers
@@ -32,7 +32,6 @@ install: all installdirs
 	$(INSTALL_DATA) $(srcdir)/postgres_ext.h   '$(DESTDIR)$(includedir)'
 	$(INSTALL_DATA) $(srcdir)/libpq/libpq-fs.h '$(DESTDIR)$(includedir)/libpq'
 	$(INSTALL_DATA) pg_config.h     '$(DESTDIR)$(includedir)'
-	$(INSTALL_DATA) pg_config_ext.h '$(DESTDIR)$(includedir)'
 	$(INSTALL_DATA) pg_config_os.h  '$(DESTDIR)$(includedir)'
 	$(INSTALL_DATA) $(srcdir)/pg_config_manual.h '$(DESTDIR)$(includedir)'
 # These headers are needed by the not-so-public headers of the interfaces.
@@ -43,7 +42,6 @@ install: all installdirs
 	$(INSTALL_DATA) $(srcdir)/libpq/protocol.h '$(DESTDIR)$(includedir_internal)/libpq'
 # These headers are needed for server-side development
 	$(INSTALL_DATA) pg_config.h     '$(DESTDIR)$(includedir_server)'
-	$(INSTALL_DATA) pg_config_ext.h '$(DESTDIR)$(includedir_server)'
 	$(INSTALL_DATA) pg_config_os.h  '$(DESTDIR)$(includedir_server)'
 	$(INSTALL_DATA) nodes/nodetags.h '$(DESTDIR)$(includedir_server)/nodes'
 	$(INSTALL_DATA) utils/errcodes.h '$(DESTDIR)$(includedir_server)/utils'
@@ -66,7 +64,7 @@ installdirs:

 uninstall:
-	rm -f $(addprefix '$(DESTDIR)$(includedir)'/, pg_config.h pg_config_ext.h pg_config_os.h pg_config_manual.h postgres_ext.h libpq/libpq-fs.h)
+	rm -f $(addprefix '$(DESTDIR)$(includedir)'/, pg_config.h pg_config_os.h pg_config_manual.h postgres_ext.h libpq/libpq-fs.h)
 	rm -f $(addprefix '$(DESTDIR)$(includedir_internal)'/, c.h port.h postgres_fe.h libpq/pqcomm.h libpq/protocol.h)
 # heuristic...
 	rm -rf $(addprefix '$(DESTDIR)$(includedir_server)'/, $(SUBDIRS) *.h)
@@ -80,4 +78,4 @@ clean:
 	$(MAKE) -C catalog clean
 
 distclean: clean
-	rm -f pg_config.h pg_config_ext.h pg_config_os.h stamp-h stamp-ext-h
+	rm -f pg_config.h pg_config_os.h stamp-h stamp-ext-h
diff --git a/src/include/c.h b/src/include/c.h
index dc1841346cd..024213f37f3 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -48,9 +48,6 @@
 
 #include "postgres_ext.h"
 
-/* Must undef pg_config_ext.h symbols before including pg_config.h */
-#undef PG_INT64_TYPE
-
 #include "pg_config.h"
 #include "pg_config_manual.h"	/* must be after pg_config.h */
 #include "pg_config_os.h"		/* must be before any system header files */
@@ -489,10 +486,13 @@ typedef char *Pointer;
  *		frontend/backend protocol.
  */
 #ifndef HAVE_INT8
-typedef signed char int8;		/* == 8 bits */
-typedef signed short int16;		/* == 16 bits */
-typedef signed int int32;		/* == 32 bits */
+typedef int8_t int8;			/* == 8 bits */
+typedef int16_t int16;			/* == 16 bits */
+typedef int32_t int32;			/* == 32 bits */
 #endif							/* not HAVE_INT8 */
+#ifndef HAVE_INT64
+typedef int64_t int64;			/* == 64 bits */
+#endif							/* not HAVE_INT64 */
 
 /*
  * uintN
@@ -501,10 +501,13 @@ typedef signed int int32;		/* == 32 bits */
  *		frontend/backend protocol.
  */
 #ifndef HAVE_UINT8
-typedef unsigned char uint8;	/* == 8 bits */
-typedef unsigned short uint16;	/* == 16 bits */
-typedef unsigned int uint32;	/* == 32 bits */
+typedef uint8_t uint8;			/* == 8 bits */
+typedef uint16_t uint16;		/* == 16 bits */
+typedef uint32_t uint32;		/* == 32 bits */
 #endif							/* not HAVE_UINT8 */
+#ifndef HAVE_UINT64
+typedef uint64_t uint64;		/* == 64 bits */
+#endif							/* not HAVE_UINT64 */
 
 /*
  * bitsN
@@ -517,32 +520,8 @@ typedef uint32 bits32;			/* >= 32 bits */
 /*
  * 64-bit integers
  */
-#ifdef HAVE_LONG_INT_64
-/* Plain "long int" fits, use it */
-
-#ifndef HAVE_INT64
-typedef long int int64;
-#endif
-#ifndef HAVE_UINT64
-typedef unsigned long int uint64;
-#endif
-#define INT64CONST(x)  (x##L)
-#define UINT64CONST(x) (x##UL)
-#elif defined(HAVE_LONG_LONG_INT_64)
-/* We have working support for "long long int", use that */
-
-#ifndef HAVE_INT64
-typedef long long int int64;
-#endif
-#ifndef HAVE_UINT64
-typedef unsigned long long int uint64;
-#endif
-#define INT64CONST(x)  (x##LL)
-#define UINT64CONST(x) (x##ULL)
-#else
-/* neither HAVE_LONG_INT_64 nor HAVE_LONG_LONG_INT_64 */
-#error must have a working 64-bit integer datatype
-#endif
+#define INT64CONST(x)  INT64_C(x)
+#define UINT64CONST(x) UINT64_C(x)
 
 /* snprintf format strings to use for 64-bit integers */
 #define INT64_FORMAT "%" INT64_MODIFIER "d"
@@ -575,22 +554,19 @@ typedef unsigned PG_INT128_TYPE uint128
 #endif
 #endif
 
-/*
- * stdint.h limits aren't guaranteed to have compatible types with our fixed
- * width types. So just define our own.
- */
-#define PG_INT8_MIN		(-0x7F-1)
-#define PG_INT8_MAX		(0x7F)
-#define PG_UINT8_MAX	(0xFF)
-#define PG_INT16_MIN	(-0x7FFF-1)
-#define PG_INT16_MAX	(0x7FFF)
-#define PG_UINT16_MAX	(0xFFFF)
-#define PG_INT32_MIN	(-0x7FFFFFFF-1)
-#define PG_INT32_MAX	(0x7FFFFFFF)
-#define PG_UINT32_MAX	(0xFFFFFFFFU)
-#define PG_INT64_MIN	(-INT64CONST(0x7FFFFFFFFFFFFFFF) - 1)
-#define PG_INT64_MAX	INT64CONST(0x7FFFFFFFFFFFFFFF)
-#define PG_UINT64_MAX	UINT64CONST(0xFFFFFFFFFFFFFFFF)
+/* Historical names for limits in stdint.h. */
+#define PG_INT8_MIN		INT8_MIN
+#define PG_INT8_MAX		INT8_MAX
+#define PG_UINT8_MAX	UINT8_MAX
+#define PG_INT16_MIN	INT16_MIN
+#define PG_INT16_MAX	INT16_MAX
+#define PG_UINT16_MAX	UINT16_MAX
+#define PG_INT32_MIN	INT32_MIN
+#define PG_INT32_MAX	INT32_MAX
+#define PG_UINT32_MAX	UINT32_MAX
+#define PG_INT64_MIN	INT64_MIN
+#define PG_INT64_MAX	INT64_MAX
+#define PG_UINT64_MAX	UINT64_MAX
 
 /*
  * We now always use int64 timestamps, but keep this symbol defined for the
diff --git a/src/include/meson.build b/src/include/meson.build
index 58b7a9c1e7e..e691b12b8a0 100644
--- a/src/include/meson.build
+++ b/src/include/meson.build
@@ -1,14 +1,5 @@
 # Copyright (c) 2022-2024, PostgreSQL Global Development Group
 
-pg_config_ext = configure_file(
-  input: 'pg_config_ext.h.meson',
-  output: 'pg_config_ext.h',
-  configuration: cdata,
-  install: true,
-  install_dir: dir_include,
-)
-configure_files += pg_config_ext
-
 pg_config_os = configure_file(
   output: 'pg_config_os.h',
   input: files('port/@0@.h'.format(portname)),
@@ -116,7 +107,6 @@ install_headers(
   'postgres_fe.h',
   'varatt.h',
   'windowapi.h',
-  pg_config_ext,
   pg_config_os,
   pg_config,
   install_dir: dir_include_server,
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index f8d3e3b6b84..3755eb69e24 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -9,12 +9,12 @@
 /* The normal alignment of `int', in bytes. */
 #undef ALIGNOF_INT
 
+/* The normal alignment of `int64_t', in bytes. */
+#undef ALIGNOF_INT64_T
+
 /* The normal alignment of `long', in bytes. */
 #undef ALIGNOF_LONG
 
-/* The normal alignment of `long long int', in bytes. */
-#undef ALIGNOF_LONG_LONG_INT
-
 /* The normal alignment of `PG_INT128_TYPE', in bytes. */
 #undef ALIGNOF_PG_INT128_TYPE
 
@@ -282,12 +282,6 @@
 /* Define to 1 if you have the `zstd' library (-lzstd). */
 #undef HAVE_LIBZSTD
 
-/* Define to 1 if `long int' works and is 64 bits. */
-#undef HAVE_LONG_INT_64
-
-/* Define to 1 if `long long int' works and is 64 bits. */
-#undef HAVE_LONG_LONG_INT_64
-
 /* Define to 1 if you have the <mbarrier.h> header file. */
 #undef HAVE_MBARRIER_H
 
@@ -570,7 +564,7 @@
 /* Define to 1 if your compiler understands _Static_assert. */
 #undef HAVE__STATIC_ASSERT
 
-/* Define to the appropriate printf length modifier for 64-bit ints. */
+/* Define to printf format modifier to use for int64_t. */
 #undef INT64_MODIFIER
 
 /* Define to 1 if `locale_t' requires <xlocale.h>. */
@@ -607,9 +601,6 @@
 /* Define to the name of a signed 128-bit integer type. */
 #undef PG_INT128_TYPE
 
-/* Define to the name of a signed 64-bit integer type. */
-#undef PG_INT64_TYPE
-
 /* Define to the name of the default PostgreSQL service principal in Kerberos
    (GSSAPI). (--with-krb-srvnam=NAME) */
 #undef PG_KRB_SRVNAM
diff --git a/src/include/pg_config_ext.h.in b/src/include/pg_config_ext.h.in
deleted file mode 100644
index 8acadbdafd4..00000000000
--- a/src/include/pg_config_ext.h.in
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * src/include/pg_config_ext.h.in.  This is generated manually, not by
- * autoheader, since we want to limit which symbols get defined here.
- */
-
-/* Define to the name of a signed 64-bit integer type. */
-#undef PG_INT64_TYPE
diff --git a/src/include/pg_config_ext.h.meson b/src/include/pg_config_ext.h.meson
deleted file mode 100644
index 57cdfca0cfd..00000000000
--- a/src/include/pg_config_ext.h.meson
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * src/include/pg_config_ext.h.in.  This is generated manually, not by
- * autoheader, since we want to limit which symbols get defined here.
- */
-
-/* Define to the name of a signed 64-bit integer type. */
-#mesondefine PG_INT64_TYPE
diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h
index 4d88478c9c2..1c09bf0ff27 100644
--- a/src/include/port/pg_bitutils.h
+++ b/src/include/port/pg_bitutils.h
@@ -74,13 +74,12 @@ pg_leftmost_one_pos64(uint64 word)
 #ifdef HAVE__BUILTIN_CLZ
 	Assert(word != 0);
 
-#if defined(HAVE_LONG_INT_64)
+#if SIZEOF_LONG == 8
 	return 63 - __builtin_clzl(word);
-#elif defined(HAVE_LONG_LONG_INT_64)
-	return 63 - __builtin_clzll(word);
 #else
-#error must have a working 64-bit integer datatype
-#endif							/* HAVE_LONG_INT_64 */
+	StaticAssertStmt(sizeof(word) == sizeof(long long), "unexpected size");
+	return 63 - __builtin_clzll(word);
+#endif
 
 #elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_ARM64))
 	unsigned long result;
@@ -147,13 +146,12 @@ pg_rightmost_one_pos64(uint64 word)
 #ifdef HAVE__BUILTIN_CTZ
 	Assert(word != 0);
 
-#if defined(HAVE_LONG_INT_64)
+#if SIZEOF_LONG == 8
 	return __builtin_ctzl(word);
-#elif defined(HAVE_LONG_LONG_INT_64)
-	return __builtin_ctzll(word);
 #else
-#error must have a working 64-bit integer datatype
-#endif							/* HAVE_LONG_INT_64 */
+	StaticAssertStmt(sizeof(word) == sizeof(long long), "unexpected size");
+	return __builtin_ctzll(word);
+#endif
 
 #elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_ARM64))
 	unsigned long result;
diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h
index 240ad4e93bf..034056e7d94 100644
--- a/src/include/postgres_ext.h
+++ b/src/include/postgres_ext.h
@@ -23,7 +23,7 @@
 #ifndef POSTGRES_EXT_H
 #define POSTGRES_EXT_H
 
-#include "pg_config_ext.h"
+#include <stdint.h>
 
 /*
  * Object ID is a fundamental type in Postgres.
@@ -36,15 +36,14 @@ typedef unsigned int Oid;
 #define InvalidOid		((Oid) 0)
 #endif
 
-#define OID_MAX  UINT_MAX
-/* you will need to include <limits.h> to use the above #define */
+#define OID_MAX  UINT32_MAX
 
 #define atooid(x) ((Oid) strtoul((x), NULL, 10))
 /* the above needs <stdlib.h> */

 /* Define a signed 64-bit integer type for use in client API declarations. */
-typedef PG_INT64_TYPE pg_int64;
+typedef int64_t pg_int64;
 
 /*
  * Identifiers of error message fields.  Kept here to keep common
diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c
index 1d482c4fa61..1ab2cda8487 100644
--- a/src/interfaces/ecpg/ecpglib/typename.c
+++ b/src/interfaces/ecpg/ecpglib/typename.c
@@ -131,10 +131,9 @@ sqlda_dynamic_type(Oid type, enum COMPAT_MODE compat)
 		case INTERVALOID:
 			return ECPGt_interval;
 		case INT8OID:
-#ifdef HAVE_LONG_LONG_INT_64
+#if SIZEOF_LONG < 8
 			return ECPGt_long_long;
-#endif
-#ifdef HAVE_LONG_INT_64
+#else
 			return ECPGt_long;
 #endif
 			/* Unhandled types always return a string */
diff --git a/src/interfaces/ecpg/include/ecpg_config.h.in b/src/interfaces/ecpg/include/ecpg_config.h.in
index 824617b9175..48bce0878a7 100644
--- a/src/interfaces/ecpg/include/ecpg_config.h.in
+++ b/src/interfaces/ecpg/include/ecpg_config.h.in
@@ -1,14 +1,5 @@
 /* Define to 1 to build client libraries as thread-safe code. */
 #define ENABLE_THREAD_SAFETY 1
 
-/* Define to 1 if the system has the type `int64'. */
-#undef HAVE_INT64
-
-/* Define to 1 if `long int' works and is 64 bits. */
-#undef HAVE_LONG_INT_64
-
-/* Define to 1 if `long long int' works and is 64 bits. */
-#undef HAVE_LONG_LONG_INT_64
-
 /* Define to 1 to use <stdbool.h> to define type bool. */
 #undef PG_USE_STDBOOL
diff --git a/src/interfaces/ecpg/include/meson.build b/src/interfaces/ecpg/include/meson.build
index 31610fef589..d0234413fcd 100644
--- a/src/interfaces/ecpg/include/meson.build
+++ b/src/interfaces/ecpg/include/meson.build
@@ -3,9 +3,6 @@
 ecpg_inc = include_directories('.')
 
 ecpg_conf_keys = [
-  'HAVE_INT64',
-  'HAVE_LONG_INT_64',
-  'HAVE_LONG_LONG_INT_64',
   'PG_USE_STDBOOL',
 ]
 
diff --git a/src/interfaces/ecpg/include/pgtypes_interval.h b/src/interfaces/ecpg/include/pgtypes_interval.h
index 2809b356f73..d16f74970c9 100644
--- a/src/interfaces/ecpg/include/pgtypes_interval.h
+++ b/src/interfaces/ecpg/include/pgtypes_interval.h
@@ -8,18 +8,7 @@
 
 #ifndef C_H
 
-#ifdef HAVE_LONG_INT_64
-#ifndef HAVE_INT64
-typedef long int int64;
-#endif
-#elif defined(HAVE_LONG_LONG_INT_64)
-#ifndef HAVE_INT64
-typedef long long int int64;
-#endif
-#else
-/* neither HAVE_LONG_INT_64 nor HAVE_LONG_LONG_INT_64 */
-#error must have a working 64-bit integer datatype
-#endif
+typedef int64_t int64;
 
 #define HAVE_INT64_TIMESTAMP
 #endif							/* C_H */
diff --git a/src/interfaces/ecpg/include/sqltypes.h b/src/interfaces/ecpg/include/sqltypes.h
index e7cbfa47956..aed055eb4ab 100644
--- a/src/interfaces/ecpg/include/sqltypes.h
+++ b/src/interfaces/ecpg/include/sqltypes.h
@@ -46,7 +46,7 @@
 #define SQLINTERVAL		ECPGt_interval
 #define SQLNCHAR	ECPGt_char
 #define SQLNVCHAR	ECPGt_char
-#ifdef HAVE_LONG_LONG_INT_64
+#if SIZEOF_LONG < 8
 #define SQLINT8		ECPGt_long_long
 #define SQLSERIAL8	ECPGt_long_long
 #else
diff --git a/src/interfaces/ecpg/test/expected/compat_informix-sqlda.c b/src/interfaces/ecpg/test/expected/compat_informix-sqlda.c
index 7e19319d27f..02ec7b668d3 100644
--- a/src/interfaces/ecpg/test/expected/compat_informix-sqlda.c
+++ b/src/interfaces/ecpg/test/expected/compat_informix-sqlda.c
@@ -97,7 +97,7 @@ typedef struct sqlda_struct sqlda_t;
 #define SQLINTERVAL		ECPGt_interval
 #define SQLNCHAR	ECPGt_char
 #define SQLNVCHAR	ECPGt_char
-#ifdef HAVE_LONG_LONG_INT_64
+#if SIZEOF_LONG < 8
 #define SQLINT8		ECPGt_long_long
 #define SQLSERIAL8	ECPGt_long_long
 #else
diff --git a/src/port/pg_bitutils.c b/src/port/pg_bitutils.c
index 87f56e82b80..d4413a299a9 100644
--- a/src/port/pg_bitutils.c
+++ b/src/port/pg_bitutils.c
@@ -370,12 +370,11 @@ static inline int
 pg_popcount64_slow(uint64 word)
 {
 #ifdef HAVE__BUILTIN_POPCOUNT
-#if defined(HAVE_LONG_INT_64)
+#if SIZEOF_LONG == 8
 	return __builtin_popcountl(word);
-#elif defined(HAVE_LONG_LONG_INT_64)
-	return __builtin_popcountll(word);
 #else
-#error must have a working 64-bit integer datatype
+	StaticAssertStmt(sizeof(word) == sizeof(long long), "unexpected size");
+	return __builtin_popcountll(word);
 #endif
 #else							/* !HAVE__BUILTIN_POPCOUNT */
 	int			result = 0;
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 884f0262dd1..291148b4e8b 100644
--- a/src/port/snprintf.c
+++ b/src/port/snprintf.c
@@ -568,12 +568,11 @@ nextch2:
 				goto nextch2;
 			case 'z':
 #if SIZEOF_SIZE_T == 8
-#ifdef HAVE_LONG_INT_64
+#if SIZEOF_LONG == 8
 				longflag = 1;
-#elif defined(HAVE_LONG_LONG_INT_64)
-				longlongflag = 1;
 #else
-#error "Don't know how to print 64bit integers"
+				StaticAssertStmt(sizeof(size_t) == sizeof(long long), "unexpected size");
+				longlongflag = 1;
 #endif
 #else
 				/* assume size_t is same size as int */
@@ -835,12 +834,11 @@ nextch1:
 				goto nextch1;
 			case 'z':
 #if SIZEOF_SIZE_T == 8
-#ifdef HAVE_LONG_INT_64
+#if SIZEOF_LONG == 8
 				longflag = 1;
-#elif defined(HAVE_LONG_LONG_INT_64)
-				longlongflag = 1;
 #else
-#error "Don't know how to print 64bit integers"
+				StaticAssertStmt(sizeof(size_t) == sizeof(long long), "unexpected size");
+				longlongflag = 1;
 #endif
 #else
 				/* assume size_t is same size as int */
-- 
2.45.2

From 2b3c15b079c5f8aebd2f6a8f861c9422db757c2b Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email-masked]>
Date: Thu, 18 Apr 2024 12:06:17 +1200
Subject: [PATCH v3 2/3] Remove traces of BeOS.

Commit 15abc7788e6 tolerated namespace pollution from BeOS system
headers.  Commit 44f902122 de-supported BeOS.  Since that stuff didn't
make it into the Meson build system, synchronize by removing from
configure.

Reviewed-by: Peter Eisentraut <[email-masked]>
Reviewed-by: Heikki Linnakangas <[email-masked]>
Reviewed-by: Japin Li <[email-masked]>
Reviewed-by: Tom Lane <[email-masked]> (the idea, not the patch)
Discussion: https://postgr.es/m/ME3P282MB3166F9D1F71F787929C0C7E7B6312%40ME3P282MB3166.AUSP282.PROD.OUTLOOK.COM
---
 configure                  | 44 --------------------------------------
 configure.ac               |  5 -----
 src/include/c.h            |  8 -------
 src/include/pg_config.h.in | 12 -----------
 4 files changed, 69 deletions(-)

diff --git a/configure b/configure
index cfec577a1c1..3bfbfdf6521 100755
--- a/configure
+++ b/configure
@@ -16784,50 +16784,6 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF

-# Some platforms predefine the types int8, int16, etc.  Only check
-# a (hopefully) representative subset.
-ac_fn_c_check_type "$LINENO" "int8" "ac_cv_type_int8" "#include <stdio.h>
-"
-if test "x$ac_cv_type_int8" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_INT8 1
-_ACEOF
-
-
-fi
-ac_fn_c_check_type "$LINENO" "uint8" "ac_cv_type_uint8" "#include <stdio.h>
-"
-if test "x$ac_cv_type_uint8" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_UINT8 1
-_ACEOF
-
-
-fi
-ac_fn_c_check_type "$LINENO" "int64" "ac_cv_type_int64" "#include <stdio.h>
-"
-if test "x$ac_cv_type_int64" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_INT64 1
-_ACEOF
-
-
-fi
-ac_fn_c_check_type "$LINENO" "uint64" "ac_cv_type_uint64" "#include <stdio.h>
-"
-if test "x$ac_cv_type_uint64" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_UINT64 1
-_ACEOF
-
-
-fi
-
-
 # Check how to format int64_t without warnings from printf.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for printf int64_t modifier" >&5
 $as_echo_n "checking for printf int64_t modifier... " >&6; }
diff --git a/configure.ac b/configure.ac
index 10bfb994d4f..0560b9ce1a7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1985,11 +1985,6 @@ if test $ac_cv_alignof_int64_t -gt $MAX_ALIGNOF ; then
 fi
 AC_DEFINE_UNQUOTED(MAXIMUM_ALIGNOF, $MAX_ALIGNOF, [Define as the maximum alignment requirement of any C data type.])
 
-# Some platforms predefine the types int8, int16, etc.  Only check
-# a (hopefully) representative subset.
-AC_CHECK_TYPES([int8, uint8, int64, uint64], [], [],
-[#include <stdio.h>])
-
 # Check how to format int64_t without warnings from printf.
 PGAC_PRINTF_INT64_MODIFIER
 
diff --git a/src/include/c.h b/src/include/c.h
index 024213f37f3..dd140d2ae57 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -485,14 +485,10 @@ typedef char *Pointer;
  *		used for numerical computations and the
  *		frontend/backend protocol.
  */
-#ifndef HAVE_INT8
 typedef int8_t int8;			/* == 8 bits */
 typedef int16_t int16;			/* == 16 bits */
 typedef int32_t int32;			/* == 32 bits */
-#endif							/* not HAVE_INT8 */
-#ifndef HAVE_INT64
 typedef int64_t int64;			/* == 64 bits */
-#endif							/* not HAVE_INT64 */
 
 /*
  * uintN
@@ -500,14 +496,10 @@ typedef int64_t int64;			/* == 64 bits */
  *		used for numerical computations and the
  *		frontend/backend protocol.
  */
-#ifndef HAVE_UINT8
 typedef uint8_t uint8;			/* == 8 bits */
 typedef uint16_t uint16;		/* == 16 bits */
 typedef uint32_t uint32;		/* == 32 bits */
-#endif							/* not HAVE_UINT8 */
-#ifndef HAVE_UINT64
 typedef uint64_t uint64;		/* == 64 bits */
-#endif							/* not HAVE_UINT64 */
 
 /*
  * bitsN
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 3755eb69e24..797f6ba3d64 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -213,12 +213,6 @@
 /* Define to 1 if you have the `inet_pton' function. */
 #undef HAVE_INET_PTON
 
-/* Define to 1 if the system has the type `int64'. */
-#undef HAVE_INT64
-
-/* Define to 1 if the system has the type `int8'. */
-#undef HAVE_INT8
-
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
@@ -465,12 +459,6 @@
 /* Define to 1 if you have the <ucred.h> header file. */
 #undef HAVE_UCRED_H
 
-/* Define to 1 if the system has the type `uint64'. */
-#undef HAVE_UINT64
-
-/* Define to 1 if the system has the type `uint8'. */
-#undef HAVE_UINT8
-
 /* Define to 1 if the system has the type `union semun'. */
 #undef HAVE_UNION_SEMUN
 
-- 
2.45.2

From 27462adab2f9971ce34972062e51baf7728dc3ea Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email-masked]>
Date: Thu, 4 Jul 2024 13:00:08 +1200
Subject: [PATCH v3 3/3] Allow tzcode to use <stdint.h> and <inttypes.h>.

Previously we removed the use of types and macros from these standard
headers when synchronizing with upstream tzcode, to that we could
continue to target C89 toolchains.  Now that all supported branches of
PostgreSQL require C99, we can drop a lot of small differences.

Reviewed-by:
Discussion: https://postgr.es/m/ME3P282MB3166F9D1F71F787929C0C7E7B6312%40ME3P282MB3166.AUSP282.PROD.OUTLOOK.COM
---
 src/timezone/README      |  14 -----
 src/timezone/localtime.c | 110 ++++++++++++++++++++-------------------
 src/timezone/zic.c       |  88 ++++++++++++++-----------------
 3 files changed, 96 insertions(+), 116 deletions(-)

diff --git a/src/timezone/README b/src/timezone/README
index dd5d5f9892a..1857f03e3dd 100644
--- a/src/timezone/README
+++ b/src/timezone/README
@@ -79,13 +79,6 @@ fixed that.)
 includes relying on configure's results rather than hand-hacked
 #defines (see private.h in particular).
 
-* Similarly, avoid relying on <stdint.h> features that may not exist on old
-systems.  In particular this means using Postgres' definitions of the int32
-and int64 typedefs, not int_fast32_t/int_fast64_t.  Likewise we use
-PG_INT32_MIN/MAX not INT32_MIN/MAX.  (Once we desupport all PG versions
-that don't require C99, it'd be practical to rely on <stdint.h> and remove
-this set of diffs; but that day is not yet.)
-
 * Since Postgres is typically built on a system that has its own copy
 of the <time.h> functions, we must avoid conflicting with those.  This
 mandates renaming typedef time_t to pg_time_t, and similarly for most
@@ -119,13 +112,6 @@ to first run the tzcode source files through a sed filter like this:
         -e 's|^\*/| */|' \
         -e 's/\bregister[ \t]//g' \
         -e 's/\bATTRIBUTE_PURE[ \t]//g' \
-        -e 's/int_fast32_t/int32/g' \
-        -e 's/int_fast64_t/int64/g' \
-        -e 's/intmax_t/int64/g' \
-        -e 's/INT32_MIN/PG_INT32_MIN/g' \
-        -e 's/INT32_MAX/PG_INT32_MAX/g' \
-        -e 's/INTMAX_MIN/PG_INT64_MIN/g' \
-        -e 's/INTMAX_MAX/PG_INT64_MAX/g' \
         -e 's/struct[ \t]+tm\b/struct pg_tm/g' \
         -e 's/\btime_t\b/pg_time_t/g' \
         -e 's/lineno/lineno_t/g' \
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c
index 0bc160ea7d7..e9adfc5da04 100644
--- a/src/timezone/localtime.c
+++ b/src/timezone/localtime.c
@@ -17,6 +17,7 @@
 #include "c.h"
 
 #include <fcntl.h>
+#include <stdint.h>
 
 #include "datatype/timestamp.h"
 #include "pgtz.h"
@@ -75,20 +76,20 @@ struct rule
 	int			r_day;			/* day number of rule */
 	int			r_week;			/* week number of rule */
 	int			r_mon;			/* month number of rule */
-	int32		r_time;			/* transition time of rule */
+	int_fast32_t r_time;		/* transition time of rule */
 };
 
 /*
  * Prototypes for static functions.
  */
 
-static struct pg_tm *gmtsub(pg_time_t const *timep, int32 offset,
+static struct pg_tm *gmtsub(pg_time_t const *timep, int_fast32_t offset,
 							struct pg_tm *tmp);
 static bool increment_overflow(int *ip, int j);
-static bool increment_overflow_time(pg_time_t *tp, int32 j);
+static bool increment_overflow_time(pg_time_t *tp, int_fast32_t j);
 static int64 leapcorr(struct state const *sp, pg_time_t t);
 static struct pg_tm *timesub(pg_time_t const *timep,
-							 int32 offset, struct state const *sp,
+							 int_fast32_t offset, struct state const *sp,
 							 struct pg_tm *tmp);
 static bool typesequiv(struct state const *sp, int a, int b);
 
@@ -105,7 +106,7 @@ static struct pg_tm tm;
 
 /* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX.  */
 static void
-init_ttinfo(struct ttinfo *s, int32 utoff, bool isdst, int desigidx)
+init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx)
 {
 	s->tt_utoff = utoff;
 	s->tt_isdst = isdst;
@@ -114,15 +115,15 @@ init_ttinfo(struct ttinfo *s, int32 utoff, bool isdst, int desigidx)
 	s->tt_ttisut = false;
 }
 
-static int32
+static int_fast32_t
 detzcode(const char *const codep)
 {
-	int32		result;
+	int_fast32_t result;
 	int			i;
-	int32		one = 1;
-	int32		halfmaxval = one << (32 - 2);
-	int32		maxval = halfmaxval - 1 + halfmaxval;
-	int32		minval = -1 - maxval;
+	int_fast32_t one = 1;
+	int_fast32_t halfmaxval = one << (32 - 2);
+	int_fast32_t maxval = halfmaxval - 1 + halfmaxval;
+	int_fast32_t minval = -1 - maxval;
 
 	result = codep[0] & 0x7f;
 	for (i = 1; i < 4; ++i)
@@ -134,21 +135,21 @@ detzcode(const char *const codep)
 		 * Do two's-complement negation even on non-two's-complement machines.
 		 * If the result would be minval - 1, return minval.
 		 */
-		result -= !TWOS_COMPLEMENT(int32) && result != 0;
+		result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0;
 		result += minval;
 	}
 	return result;
 }
 
-static int64
+static int_fast64_t
 detzcode64(const char *const codep)
 {
-	uint64		result;
+	int_fast64_t result;
 	int			i;
-	int64		one = 1;
-	int64		halfmaxval = one << (64 - 2);
-	int64		maxval = halfmaxval - 1 + halfmaxval;
-	int64		minval = -TWOS_COMPLEMENT(int64) - maxval;
+	int_fast64_t one = 1;
+	int_fast64_t halfmaxval = one << (64 - 2);
+	int_fast64_t maxval = halfmaxval - 1 + halfmaxval;
+	int_fast64_t minval = -TWOS_COMPLEMENT(int_fast64_t) - maxval;
 
 	result = codep[0] & 0x7f;
 	for (i = 1; i < 8; ++i)
@@ -160,7 +161,7 @@ detzcode64(const char *const codep)
 		 * Do two's-complement negation even on non-two's-complement machines.
 		 * If the result would be minval - 1, return minval.
 		 */
-		result -= !TWOS_COMPLEMENT(int64) && result != 0;
+		result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0;
 		result += minval;
 	}
 	return result;
@@ -246,14 +247,14 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
 		return errno;
 	for (stored = 4; stored <= 8; stored *= 2)
 	{
-		int32		ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
-		int32		ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
-		int64		prevtr = 0;
-		int32		prevcorr = 0;
-		int32		leapcnt = detzcode(up->tzhead.tzh_leapcnt);
-		int32		timecnt = detzcode(up->tzhead.tzh_timecnt);
-		int32		typecnt = detzcode(up->tzhead.tzh_typecnt);
-		int32		charcnt = detzcode(up->tzhead.tzh_charcnt);
+		int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
+		int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
+		int_fast64_t prevtr = 0;
+		int_fast32_t prevcorr = 0;
+		int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt);
+		int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt);
+		int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt);
+		int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt);
 		char const *p = up->buf + tzheadsize;
 
 		/*
@@ -350,8 +351,8 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
 		leapcnt = 0;
 		for (i = 0; i < sp->leapcnt; ++i)
 		{
-			int64		tr = stored == 4 ? detzcode(p) : detzcode64(p);
-			int32		corr = detzcode(p + stored);
+			int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p);
+			int_fast32_t corr = detzcode(p + stored);
 
 			p += stored + 4;
 			/* Leap seconds cannot occur before the Epoch.  */
@@ -361,7 +362,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
 			{
 				/*
 				 * Leap seconds cannot occur more than once per UTC month, and
-				 * UTC months are at least 28 days long (minus 1 second for a
+				 * UTC months are at least 2 days long (minus 1 second for a
 				 * negative leap second).  Each leap second's correction must
 				 * differ from the previous one's by 1 second.
 				 */
@@ -707,9 +708,10 @@ getnum(const char *strp, int *const nump, const int min, const int max)
  */
 
 static const char *
-getsecs(const char *strp, int32 *const secsp)
+getsecs(const char *strp, int_fast32_t * const secsp)
 {
 	int			num;
+	int_fast32_t secsperhour = SECSPERHOUR;
 
 	/*
 	 * 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
@@ -719,7 +721,7 @@ getsecs(const char *strp, int32 *const secsp)
 	strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
 	if (strp == NULL)
 		return NULL;
-	*secsp = num * (int32) SECSPERHOUR;
+	*secsp = num * secsperhour;
 	if (*strp == ':')
 	{
 		++strp;
@@ -748,7 +750,7 @@ getsecs(const char *strp, int32 *const secsp)
  */
 
 static const char *
-getoffset(const char *strp, int32 *const offsetp)
+getoffset(const char *strp, int_fast32_t * const offsetp)
 {
 	bool		neg = false;
 
@@ -835,12 +837,12 @@ getrule(const char *strp, struct rule *const rulep)
  * effect, calculate the year-relative time that rule takes effect.
  */
 
-static int32
+static int_fast32_t
 transtime(const int year, const struct rule *const rulep,
-		  const int32 offset)
+		  const int_fast32_t offset)
 {
 	bool		leapyear;
-	int32		value;
+	int_fast32_t value;
 	int			i;
 	int			d,
 				m1,
@@ -940,8 +942,8 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 	size_t		stdlen;
 	size_t		dstlen;
 	size_t		charcnt;
-	int32		stdoffset;
-	int32		dstoffset;
+	int_fast32_t stdoffset;
+	int_fast32_t dstoffset;
 	char	   *cp;
 	bool		load_ok;
 
@@ -1033,7 +1035,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 			int			yearlim;
 			int			timecnt;
 			pg_time_t	janfirst;
-			int32		janoffset = 0;
+			int_fast32_t janoffset = 0;
 			int			yearbeg;
 
 			++name;
@@ -1059,7 +1061,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 
 			do
 			{
-				int32		yearsecs
+				int_fast32_t yearsecs
 				= year_lengths[isleap(yearbeg - 1)] * SECSPERDAY;
 
 				yearbeg--;
@@ -1073,17 +1075,17 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 			yearlim = yearbeg + YEARSPERREPEAT + 1;
 			for (year = yearbeg; year < yearlim; year++)
 			{
-				int32
-							starttime = transtime(year, &start, stdoffset),
-							endtime = transtime(year, &end, dstoffset);
-				int32
-							yearsecs = (year_lengths[isleap(year)]
-										* SECSPERDAY);
+				int_fast32_t
+					starttime = transtime(year, &start, stdoffset),
+					endtime = transtime(year, &end, dstoffset);
+				int_fast32_t
+					yearsecs = (year_lengths[isleap(year)]
+								* SECSPERDAY);
 				bool		reversed = endtime < starttime;
 
 				if (reversed)
 				{
-					int32		swap = starttime;
+					int_fast32_t swap = starttime;
 
 					starttime = endtime;
 					endtime = swap;
@@ -1126,9 +1128,9 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 		}
 		else
 		{
-			int32		theirstdoffset;
-			int32		theirdstoffset;
-			int32		theiroffset;
+			int_fast32_t theirstdoffset;
+			int_fast32_t theirdstoffset;
+			int_fast32_t theiroffset;
 			bool		isdst;
 			int			i;
 			int			j;
@@ -1290,7 +1292,7 @@ localsub(struct state const *sp, pg_time_t const *timep,
 		result = localsub(sp, &newt, tmp);
 		if (result)
 		{
-			int64		newy;
+			int_fast64_t newy;
 
 			newy = result->tm_year;
 			if (t < sp->ats[0])
@@ -1354,7 +1356,7 @@ pg_localtime(const pg_time_t *timep, const pg_tz *tz)
  */
 
 static struct pg_tm *
-gmtsub(pg_time_t const *timep, int32 offset,
+gmtsub(pg_time_t const *timep, int_fast32_t offset,
 	   struct pg_tm *tmp)
 {
 	struct pg_tm *result;
@@ -1411,7 +1413,7 @@ leaps_thru_end_of(const int y)
 }
 
 static struct pg_tm *
-timesub(const pg_time_t *timep, int32 offset,
+timesub(const pg_time_t *timep, int_fast32_t offset,
 		const struct state *sp, struct pg_tm *tmp)
 {
 	const struct lsinfo *lp;
@@ -1554,7 +1556,7 @@ increment_overflow(int *ip, int j)
 }
 
 static bool
-increment_overflow_time(pg_time_t *tp, int32 j)
+increment_overflow_time(pg_time_t *tp, int_fast32_t j)
 {
 	/*----------
 	 * This is like
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
index d605c721ecf..0a98bb0ebb6 100644
--- a/src/timezone/zic.c
+++ b/src/timezone/zic.c
@@ -11,6 +11,8 @@
 #include "postgres_fe.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
 #include <sys/stat.h>
 #include <time.h>
 
@@ -22,12 +24,16 @@
 #define	ZIC_VERSION_PRE_2013 '2'
 #define	ZIC_VERSION	'3'
 
-typedef int64 zic_t;
-#define ZIC_MIN PG_INT64_MIN
-#define ZIC_MAX PG_INT64_MAX
+typedef int_fast64_t zic_t;
+static zic_t const
+			ZIC_MIN = INT_FAST64_MIN,
+			ZIC_MAX = INT_FAST64_MAX,
+			ZIC32_MIN = INT32_MIN,
+			ZIC32_MAX = INT32_MAX;
+#define SCNdZIC SCNdFAST64
 
 #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
-#define ZIC_MAX_ABBR_LEN_WO_WARN	6
+#define ZIC_MAX_ABBR_LEN_WO_WARN 6
 #endif							/* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
 
 #ifndef WIN32
@@ -601,7 +607,7 @@ static zic_t comment_leapexpires = -1;
 static bool
 timerange_option(char *timerange)
 {
-	int64		lo = min_time,
+	intmax_t	lo = min_time,
 				hi = max_time;
 	char	   *lo_end = timerange,
 			   *hi_end;
@@ -610,7 +616,7 @@ timerange_option(char *timerange)
 	{
 		errno = 0;
 		lo = strtoimax(timerange + 1, &lo_end, 10);
-		if (lo_end == timerange + 1 || (lo == PG_INT64_MAX && errno == ERANGE))
+		if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE))
 			return false;
 	}
 	hi_end = lo_end;
@@ -618,9 +624,9 @@ timerange_option(char *timerange)
 	{
 		errno = 0;
 		hi = strtoimax(lo_end + 2, &hi_end, 10);
-		if (hi_end == lo_end + 2 || hi == PG_INT64_MIN)
+		if (hi_end == lo_end + 2 || hi == INTMAX_MIN)
 			return false;
-		hi -= !(hi == PG_INT64_MAX && errno == ERANGE);
+		hi -= !(hi == INTMAX_MAX && errno == ERANGE);
 	}
 	if (*hi_end || hi < lo || max_time < lo || hi < min_time)
 		return false;
@@ -1291,18 +1297,7 @@ infile(const char *name)
 		{
 			if (name == leapsec && *buf == '#')
 			{
-				/*
-				 * PG: INT64_FORMAT isn't portable for sscanf, so be content
-				 * with scanning a "long".  Once we are requiring C99 in all
-				 * live branches, it'd be sensible to adopt upstream's
-				 * practice of using the <inttypes.h> macros.  But for now, we
-				 * don't actually use this code, and it won't overflow before
-				 * 2038 anyway.
-				 */
-				long		cl_tmp;
-
-				sscanf(buf, "#expires %ld", &cl_tmp);
-				comment_leapexpires = cl_tmp;
+				sscanf(buf, "#expires %" SCNdZIC, &comment_leapexpires);
 			}
 		}
 		else if (wantcont)
@@ -1364,8 +1359,7 @@ infile(const char *name)
 static zic_t
 gethms(char const *string, char const *errstring)
 {
-	/* PG: make hh be int not zic_t to avoid sscanf portability issues */
-	int			hh;
+	zic_t		hh;
 	int			sign,
 				mm = 0,
 				ss = 0;
@@ -1387,7 +1381,7 @@ gethms(char const *string, char const *errstring)
 	else
 		sign = 1;
 	switch (sscanf(string,
-				   "%d%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
+				   "%" SCNdZIC "%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
 				   &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs))
 	{
 		default:
@@ -1424,7 +1418,7 @@ gethms(char const *string, char const *errstring)
 		return 0;
 	}
 	/* Some compilers warn that this test is unsatisfiable for 32-bit ints */
-#if INT_MAX > PG_INT32_MAX
+#if INT_MAX > INT32_MAX
 	if (ZIC_MAX / SECSPERHOUR < hh)
 	{
 		error(_("time overflow"));
@@ -1670,8 +1664,7 @@ getleapdatetime(char **fields, int nfields, bool expire_line)
 	zic_t		i,
 				j;
 
-	/* PG: make year be int not zic_t to avoid sscanf portability issues */
-	int			year;
+	zic_t		year;
 	int			month,
 				day;
 	zic_t		dayoff,
@@ -1681,7 +1674,7 @@ getleapdatetime(char **fields, int nfields, bool expire_line)
 
 	dayoff = 0;
 	cp = fields[LP_YEAR];
-	if (sscanf(cp, "%d%c", &year, &xs) != 1)
+	if (sscanf(cp, "%" SCNdZIC "%c", &year, &xs) != 1)
 	{
 		/*
 		 * Leapin' Lizards!
@@ -1830,9 +1823,6 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
 	char	   *ep;
 	char		xs;
 
-	/* PG: year_tmp is to avoid sscanf portability issues */
-	int			year_tmp;
-
 	if ((lp = byword(monthp, mon_names)) == NULL)
 	{
 		error(_("invalid month name"));
@@ -1890,9 +1880,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
 						progname, lp->l_value);
 				exit(EXIT_FAILURE);
 		}
-	else if (sscanf(cp, "%d%c", &year_tmp, &xs) == 1)
-		rp->r_loyear = year_tmp;
-	else
+	else if (sscanf(cp, "%" SCNdZIC "%c", &rp->r_loyear, &xs) != 1)
 	{
 		error(_("invalid starting year"));
 		return;
@@ -1918,9 +1906,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
 						progname, lp->l_value);
 				exit(EXIT_FAILURE);
 		}
-	else if (sscanf(cp, "%d%c", &year_tmp, &xs) == 1)
-		rp->r_hiyear = year_tmp;
-	else
+	else if (sscanf(cp, "%" SCNdZIC "%c", &rp->r_hiyear, &xs) != 1)
 	{
 		error(_("invalid ending year"));
 		return;
@@ -1989,7 +1975,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
 }
 
 static void
-convert(const int32 val, char *const buf)
+convert(const int_fast32_t val, char *const buf)
 {
 	int			i;
 	int			shift;
@@ -2000,7 +1986,7 @@ convert(const int32 val, char *const buf)
 }
 
 static void
-convert64(const zic_t val, char *const buf)
+convert64(uint_fast64_t val, char *const buf)
 {
 	int			i;
 	int			shift;
@@ -2011,7 +1997,7 @@ convert64(const zic_t val, char *const buf)
 }
 
 static void
-puttzcode(const int32 val, FILE *const fp)
+puttzcode(zic_t val, FILE *const fp)
 {
 	char		buf[4];
 
@@ -2201,7 +2187,7 @@ writezone(const char *const name, const char *const string, char version,
 	rangeall.count = timecnt;
 	rangeall.leapcount = leapcnt;
 	range64 = limitrange(rangeall, lo_time, hi_time, ats, types);
-	range32 = limitrange(range64, PG_INT32_MIN, PG_INT32_MAX, ats, types);
+	range32 = limitrange(range64, ZIC32_MIN, ZIC32_MAX, ats, types);
 
 	/*
 	 * Remove old file, if any, to snap links.
@@ -2263,7 +2249,7 @@ writezone(const char *const name, const char *const string, char version,
 			/*
 			 * Arguably the default time type in the 32-bit data should be
 			 * range32.defaulttype, which is suited for timestamps just before
-			 * PG_INT32_MIN.  However, zic traditionally used the time type of
+			 * INT32_MIN.  However, zic traditionally used the time type of
 			 * the indefinite past instead.  Internet RFC 8532 says readers
 			 * should ignore 32-bit data, so this discrepancy matters only to
 			 * obsolete readers where the traditional type might be more
@@ -2271,7 +2257,7 @@ writezone(const char *const name, const char *const string, char version,
 			 * value, unless -r specifies a low cutoff that excludes some
 			 * 32-bit timestamps.
 			 */
-			thisdefaulttype = (lo_time <= PG_INT32_MIN
+			thisdefaulttype = (lo_time <= INT32_MIN
 							   ? range64.defaulttype
 							   : range32.defaulttype);
 
@@ -2280,8 +2266,8 @@ writezone(const char *const name, const char *const string, char version,
 			toomanytimes = thistimecnt >> 31 >> 1 != 0;
 			thisleapi = range32.leapbase;
 			thisleapcnt = range32.leapcount;
-			locut = PG_INT32_MIN < lo_time;
-			hicut = hi_time < PG_INT32_MAX;
+			locut = INT32_MIN < lo_time;
+			hicut = hi_time < INT32_MAX;
 		}
 		else
 		{
@@ -2465,7 +2451,13 @@ writezone(const char *const name, const char *const string, char version,
 			continue;
 		}
 
-		/* PG: print current timezone abbreviations if requested */
+		/*
+		 * PG: print current timezone abbreviations if requested.  Note that
+		 * we cast to int64_t, because int_fast64_t is not required to be
+		 * compatible with INT64_FORMAT.  We can't use PRIdFAST64 here because
+		 * we override fprintf with our own implementation that might not
+		 * understand it.
+		 */
 		if (print_abbrevs && pass == 2)
 		{
 			/* Print "type" data for periods ending after print_cutoff */
@@ -2478,7 +2470,7 @@ writezone(const char *const name, const char *const string, char version,
 
 					fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
 							thisabbrev,
-							utoffs[tm],
+							(int64_t) utoffs[tm],
 							isdsts[tm] ? "\tD" : "");
 				}
 			}
@@ -2490,7 +2482,7 @@ writezone(const char *const name, const char *const string, char version,
 
 				fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
 						thisabbrev,
-						utoffs[tm],
+						(int64_t) utoffs[tm],
 						isdsts[tm] ? "\tD" : "");
 			}
 		}
@@ -2499,7 +2491,7 @@ writezone(const char *const name, const char *const string, char version,
 		 * Output a LO_TIME transition if needed; see limitrange. But do not
 		 * go below the minimum representable value for this pass.
 		 */
-		lo = pass == 1 && lo_time < PG_INT32_MIN ? PG_INT32_MIN : lo_time;
+		lo = pass == 1 && lo_time < ZIC32_MIN ? ZIC32_MIN : lo_time;
 
 		if (locut)
 			puttzcodepass(lo, fp, pass);
-- 
2.45.2

---

From: Tom Lane <[email-masked]>
Date: Wed, 03 Jul 2024 23:10:34 -0400

Thomas Munro <[email-masked]> writes:
> required C99.

Unless you've specifically checked that this reduces diffs against
upstream tzcode, I'd really prefer not to touch that code right now.
I know I'm overdue for a round of syncing src/timezone/ with upstream,
but I can't see how drive-by changes will make that easier.

> IMHO it's a rather scary choice on tzcode's part to use int_fastN_t,

Yeah, I was never pleased with that choice of theirs.  OTOH, I've
seen darn few portability complaints on their mailing list, so
it seems like they've got it right in isolation.  The problem
from our standpoint is that I don't think we want int_fastN_t
to leak into APIs visible to the rest of Postgres, because then
we risk issues related to their configuration methods being
totally unlike ours.

			regards, tom lane

---

From: Thomas Munro <[email-masked]>
Date: Thu, 4 Jul 2024 15:44:06 +1200

> but I can't see how drive-by changes will make that easier.

Sure, I'll wait until you say it's a good time.  It does remove a
dozen or so hunks of difference, which should hopefully make that job
easier eventually but I don't want to get in your way.  I can see
there are a few more trivialities we could synchronise on, like const
keywords, to kill useless diffs (either dropping local improvements or
sending patches upstream).

> we risk issues related to their configuration methods being

Yeah.  My first swing at this touched only .c files, no .h files, with
that in mind.

---

From: Peter Eisentraut <[email-masked]>
Date: Sun, 14 Jul 2024 15:47:50 +0100

> any header-only way to do that.

src/port/snprintf.c used to support I64 in the past, but it was later 
removed.  We could probably put it back.

>> nice either, though, and following standards is good, so I'm sure I'll

Using PRId64 would be very beneficial because gettext understands it, 
and so we would no longer need the various workarounds for not putting 
INT64_FORMAT into the middle of a translated string.

But this could be a separate patch.  What you have works for now.

Here are some other comments on this patch set:

* v3-0001-Use-int64_t-support-from-stdint.h.patch

- src/include/c.h:

Maybe add a comment that above all the int8_t -> int8 etc. typedefs
that these are for backward compatibility or something like that.
Actually, just move the comment

up a bit to it covers the types as well.

Also, these /* == 8 bits */ comments could be removed, I think.

- src/include/port/pg_bitutils.h:
- src/port/pg_bitutils.c:
- src/port/snprintf.c:

These changes look functionally correct, but I think I like the old
code layout better, like

#if (using long)
...
#elif (using long long)
...
#else
#error
#endif

rather than

#if (using long)
...
#else
static assertion
... // long long
#endif

which seems a bit more complicated.  I think you could leave the code
mostly alone and just change

defined(HAVE_LONG_INT_64) to SIZEOF_LONG == 8
defined(HAVE_LONG_LONG_INT_64) to SIZEOF_LONG_LONG == 8

in each case.

- src/include/postgres_ext.h:

If the type Oid is unsigned int, then the OID_MAX should be UINT_MAX.
So this should not be changed.  Also, is the comment about <limits.h>
no longer applicable?

- src/interfaces/ecpg/ecpglib/typename.c:
- src/interfaces/ecpg/include/sqltypes.h:
- .../test/expected/compat_informix-sqlda.c:

These changes alter the library behavior unnecessarily.  The old code
would always prefer to report back long long (ECPGt_long_long etc.),
but the new code will report back long (ECPGt_long etc.) if it is
64-bit.  I don't know the impact of these changes, but it seems
preferable to keep the existing behavior.

- src/interfaces/ecpg/include/ecpg_config.h.in:
- src/interfaces/ecpg/include/meson.build:

In the past, we have kept obsolete symbols as always defined in
ecpg_config.h.  We ought to do the same here.

* v3-0002-Remove-traces-of-BeOS.patch

This looks ok.  This could also be committed before 0001.

* v3-0003-Allow-tzcode-to-use-stdint.h-and-inttypes.h.patch

- src/timezone/localtime.c:

Addition of #include <stdint.h> is unnecessary, since it's already
included in c.h, and it's also not in the upstream code.

This looks like a typo:

-                                * UTC months are at least 28 days long 
(minus 1 second for a
+                                * UTC months are at least 2 days long 
(minus 1 second for a

Need to add int_fast32_t (and maybe the other types) to typedefs.list?

- src/timezone/zic.c:

+#include <inttypes.h>
+#include <stdint.h>

We don't need both of these.  Also, this is not in the upstream code
AFAICT.

---

From: Tom Lane <[email-masked]>
Date: Sun, 14 Jul 2024 10:51:54 -0400

> INT64_FORMAT into the middle of a translated string.

Uh, really?  The translated strings live in /usr/share, which is
expected to be architecture-independent, so how would they make
that work?

			regards, tom lane

---

From: Peter Eisentraut <[email-masked]>
Date: Mon, 22 Jul 2024 16:39:13 +0200

> expected to be architecture-independent, so how would they make

Gettext has some special run-time support for this.  See here: 
<https://www.gnu.org/software/gettext/manual/html_node/Preparing-Strings.html#No-string-concatenation>

---

From: Peter Eisentraut <[email-masked]>
Date: Mon, 14 Oct 2024 08:46:07 +0200

To move this along a bit, I have committed the "Remove traces of BeOS." 
patch.

---

From: Thomas Munro <[email-masked]>
Date: Fri, 29 Nov 2024 23:12:57 +1300

> <https://www.gnu.org/software/gettext/manual/html_node/Preparing-Strings.html#No-string-concatenation>

Nice.  I've never really looked into gettext() very seriously, so I
wondered about the portability of it all.  In our docs, a long time
ago, you wrote "... you need an implementation of the Gettext API.
Some operating systems have this built-in (e.g., Linux, NetBSD,
Solaris),".  It looks like we might still have those implementations
of libintl.so in our build farm today: mostly the GNU one, but also
NetBSD's, illumos's, and to the unknowable extent it is different
Solaris's.  They might all be using GNU msgfmt though.

I want to move this forward, but we have to decide which way.  I'm now
leaning back towards using <inttypes.h> macros, based on your
feedback.  So here's a rebase of v2, with some improvements, and some
responses to your earlier email:

> removed.  We could probably put it back.

New idea: let's just redefine PRI...{32,64,PTR} on that platform,
instead of modifying snprintf.c.

> up a bit to it covers the types as well.

Hmm I think it deserves a separate comment but yeah "EXACTLY N BITS"
and can be "used for numerical computations" can definitely go.  I
guess that's a memory of some pre-standard system that had similarly
named types with the "at least" meaning, but it's not helping anybody
today.

> Also, these /* == 8 bits */ comments could be removed, I think.

Yeah.

> These changes look functionally correct, but I think I like the old

Fair point, that was all a bit ugly.  Let's see...

> defined(HAVE_LONG_LONG_INT_64) to SIZEOF_LONG_LONG == 8

What do you think about this approach?

            case 'z':
#if SIZEOF_SIZE_T == SIZEOF_LONG
                longflag = 1;
#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
                longlongflag = 1;
#else
#error "cannot find integer type of the same size as size_t"
#endif

And then in the cases where the size is implied by the typename
(uint64_t -> 8) it's the same setup except with a number:

#if SIZEOF_LONG == 8
    return 63 - __builtin_clzl(word);
#elif SIZEOF_LONG_LONG == 8
    return 63 - __builtin_clzll(word);
#else
#error "cannot find integer type of the same size as uint64_t"
#endif

What I like about that is that it's very clear that the conditions
match the ...l() or ...ll() function.  It's obviously correct, and
would fail on a Deathstation 9000 that is trying to trick us.

> So this should not be changed.  Also, is the comment about <limits.h>

Right, yeah, hunk nuked.

> preferable to keep the existing behavior.

I agree that it should be better but I don't think that's right about
the preference.  The order of the tests here didn't matter, because
only HAVE_LONG_INT_64 would have been defined if sizeof(long) == 8,
not both, so it would still have preferred the first of { long, long
long } that had size 8 in that order.  But yeah that's confusing,
let's try changing it to the same form as the cases above (an
explicitly search for the integer type of the same size where the
conditions are obviously correct with a clear priority and no
default/fallback/assumption, just an error):

        case INT8OID:
#if SIZEOF_LONG == 8
            return ECPGt_long;
#elif SIZEOF_LONG_LONG == 8
            return ECPGt_long_long;
#else
#error "cannot find integer type of the same size as INT8OID"
#endif

> ecpg_config.h.  We ought to do the same here.

Normally we keep symbols that were once conditional and are now
constant, like ENABLE_THREAD_SAFETY.   Should that include
HAVE_LONG_INT_64 and HAVE_LONG_LONG_INT_64?  Only one would be
defined, but it might be that neither is true in the patch: it's a
typedef for int64_t, and the standard library might well have a
typedef to __internal_int64_type, which is neither long nor long long.
it seems like internal machinery for giving the client code an "int64"
type in the C89 days, and surely not part of our real API.

WIP patch attached.

(Let's leave that tzcode stuff far later.)

---

From: Thomas Munro <[email-masked]>
Date: Sat, 30 Nov 2024 08:30:41 +1300

> WIP patch attached.

Slightly better version.  I'd missed some configure stuff that could
be removed, and tidied up a few minor typos and mistakes.

I was thinking about that ECPG stuff: I bet real applications prefer
to use int64_t etc directly too instead of long, the worst type in C.
I wondered if the embedded SQL standard might know about that these
days (ECPGt_int64_t?), but I don't have the standard to hand.  DB2's
embedded SQL seems to have a type sqlint64, but I didn't look too
closely and of course even if we wanted to do something like that as
an optional API option, that'd be a later change.

BTW I forgot to mention earlier, I peeked at the source of gettext on
NetBSD and illumos, and both appear to handle those special
<inttypes.h> tokens when loading message catalogues.

---

From: Thomas Munro <[email-masked]>
Date: Sat, 30 Nov 2024 12:42:53 +1300

> instead of modifying snprintf.c.

D'oh, that's not going to fly.  gettext() would replace %<PRId64> with
the system's PRId64, so we can't avoid teaching our snprintf.c to
understand Windowsian format strings.  Here's a first attempt at that.
Tested a bit by removing the #ifdef WIN32 locally and playing around
with it.  CI passes on Windows, and I think that should be exercising
it via existing [U]INT64_FORMAT in various places that would break if
it didn't work.

It would be nice to see it working via gettext() too, but NLS seems to
be disabled on that OS in CI (and also the BF).  Huh, that seems
non-ideal.  Transcripts on pgsql-bugs show it working on that OS
though (with occasional encoding glitches, apparently), so I guess the
EDB build must have it enabled.

I tried out the gettext() support for this on Unix.  I didn't bother
with xgettext etc, I just manually added a message to fr.po to test
the runtime based on an example in another project, and it worked
unsurprisingly:

-                                errmsg("REJECT_LIMIT (%lld) must be
greater than zero",
-                                               (long long) reject_limit)));
+                                errmsg("REJECT_LIMIT (%" PRId64 ")
must be greater than zero",
+                                               reject_limit)));

+#, c-format
+msgid "REJECT_LIMIT (%<PRId64>) must be greater than zero"
+msgstr "REJECT_LIMIT (%<PRId64>) doit être supérieure à zéro"

postgres=# copy t from '/dev/null' with (reject_limit -1);
ERREUR:  REJECT_LIMIT (-1) doit être supérieure à zéro
From 22e6601f345285e0b823771d5f8acde5d59709e7 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email-masked]>
Date: Thu, 18 Apr 2024 07:53:10 +1200
Subject: [PATCH v6] Use <stdint.h> and <inttypes.h> for integer types.

Use standard types and support for 64 bit integers, including int64_t,
INT64_MAX, INT64_C(), PRId64 etc.  Also use standard definitions of the
smaller integers.

One complication is that Windows' <inttypes.h> uses format strings like
%I64d, %I32, %I for PRI*64, PRI*32, PTR*PTR, rather than standard format
strings like %ld as seen on other systems.  We have to teach our own
snprintf.c to recognize them.

Work in progress...

Reviewed-by: Peter Eisentraut <[email-masked]>
Discussion: https://postgr.es/m/ME3P282MB3166F9D1F71F787929C0C7E7B6312%40ME3P282MB3166.AUSP282.PROD.OUTLOOK.COM
---
 config/c-compiler.m4                          |  78 +---
 configure                                     | 354 +++++-------------
 configure.ac                                  |  52 +--
 meson.build                                   |  44 +--
 src/Makefile.global.in                        |   6 -
 src/bin/pg_waldump/pg_waldump.c               |  16 +-
 src/bin/pgbench/pgbench.c                     |   4 +-
 src/include/.gitignore                        |   2 -
 src/include/Makefile                          |   8 +-
 src/include/c.h                               | 101 ++---
 src/include/meson.build                       |  10 -
 src/include/pg_config.h.in                    |  25 +-
 src/include/pg_config_ext.h.in                |   7 -
 src/include/pg_config_ext.h.meson             |   7 -
 src/include/port/pg_bitutils.h                |  16 +-
 src/include/postgres_ext.h                    |   4 +-
 src/include/utils/dsa.h                       |   2 +-
 src/interfaces/ecpg/ecpglib/typename.c        |   9 +-
 src/interfaces/ecpg/include/ecpg_config.h.in  |   8 +-
 src/interfaces/ecpg/include/meson.build       |   4 +-
 .../ecpg/include/pgtypes_interval.h           |  12 +-
 src/interfaces/ecpg/include/sqltypes.h        |   8 +-
 .../test/expected/compat_informix-sqlda.c     |   8 +-
 src/port/pg_bitutils.c                        |   6 +-
 src/port/snprintf.c                           |  64 +++-
 25 files changed, 274 insertions(+), 581 deletions(-)
 delete mode 100644 src/include/pg_config_ext.h.in
 delete mode 100644 src/include/pg_config_ext.h.meson

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 309d5b04b46..e112fd45d48 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -38,60 +38,6 @@ ac_c_werror_flag=$ac_save_c_werror_flag
 ])# PGAC_TEST_PRINTF_ARCHETYPE

 # Check if __int128 is a working 128 bit integer type, and if so
@@ -270,9 +216,10 @@ fi])# PGAC_C_BUILTIN_CONSTANT_P
 [AC_LINK_IFELSE([AC_LANG_PROGRAM([
@@ -557,13 +504,13 @@ fi])# PGAC_HAVE_GCC__SYNC_INT32_CAS
-  AC_DEFINE(HAVE_GCC__SYNC_INT64_CAS, 1, [Define to 1 if you have __sync_val_compare_and_swap(int64 *, int64, int64).])
+  AC_DEFINE(HAVE_GCC__SYNC_INT64_CAS, 1, [Define to 1 if you have __sync_val_compare_and_swap(int64_t *, int64_t, int64_t).])
 fi])# PGAC_HAVE_GCC__SYNC_INT64_CAS
 
 # PGAC_HAVE_GCC__ATOMIC_INT32_CAS
@@ -588,9 +535,9 @@ fi])# PGAC_HAVE_GCC__ATOMIC_INT32_CAS
 # types, and define HAVE_GCC__ATOMIC_INT64_CAS if so.
 AC_DEFUN([PGAC_HAVE_GCC__ATOMIC_INT64_CAS],
 [AC_CACHE_CHECK(for builtin __atomic int64 atomic operations, pgac_cv_gcc_atomic_int64_cas,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
-  [PG_INT64_TYPE val = 0;
-   PG_INT64_TYPE expect = 0;
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <stdint.h>],
+  [int64_t val = 0;
+   int64_t expect = 0;
    __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);])],
   [pgac_cv_gcc_atomic_int64_cas="yes"],
   [pgac_cv_gcc_atomic_int64_cas="no"])])
@@ -734,13 +681,14 @@ AC_DEFUN([PGAC_AVX512_POPCNT_INTRINSICS],
 [define([Ac_cachevar], [AS_TR_SH([pgac_cv_avx512_popcnt_intrinsics])])dnl
 AC_CACHE_CHECK([for _mm512_popcnt_epi64], [Ac_cachevar],
 [AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <immintrin.h>
+    #include <stdint.h>
     #if defined(__has_attribute) && __has_attribute (target)
     __attribute__((target("avx512vpopcntdq,avx512bw")))
     #endif
     static int popcount_test(void)
     {
       const char buf@<:@sizeof(__m512i)@:>@;
-      PG_INT64_TYPE popcnt = 0;
+      int64_t popcnt = 0;
       __m512i accum = _mm512_setzero_si512();
       const __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);
       const __m512i cnt = _mm512_popcnt_epi64(val);
diff --git a/configure b/configure
index ff59f1422d8..bc49cebc990 100755
--- a/configure
+++ b/configure
@@ -14436,6 +14436,43 @@ if test x"$pgac_cv__builtin_constant_p" = xyes ; then

+fi
+oflo = __builtin_mul_overflow(a, b, &result);
@@ -16196,236 +16233,6 @@ fi
 # Run tests below here
 # --------------------
 
-
@@ -16526,6 +16333,39 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF

+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5
+$as_echo_n "checking size of long long... " >&6; }
+if ${ac_cv_sizeof_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_long_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long long)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_long_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5
+$as_echo "$ac_cv_sizeof_long_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long
+_ACEOF
+
+
 
 # Determine memory alignment requirements for the basic C data types.
 
@@ -16634,43 +16474,41 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF

 else
 
 fi

-fi
@@ -16728,8 +16566,8 @@ MAX_ALIGNOF=$ac_cv_alignof_double
 
 cat >>confdefs.h <<_ACEOF
@@ -16737,7 +16575,6 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF

-
 # Some compilers offer a 128-bit integer scalar type.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __int128" >&5
 $as_echo_n "checking for __int128... " >&6; }
@@ -16992,12 +16829,12 @@ if ${pgac_cv_gcc_sync_int64_cas+:} false; then :
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-
+#include <stdint.h>
 int
 main ()
 {
-PG_INT64_TYPE lock = 0;
-   __sync_val_compare_and_swap(&lock, 0, (PG_INT64_TYPE) 37);
+int64_t lock = 0;
+   __sync_val_compare_and_swap(&lock, 0, (int64_t) 37);
   ;
   return 0;
 }
@@ -17057,12 +16894,12 @@ if ${pgac_cv_gcc_atomic_int64_cas+:} false; then :
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-
+#include <stdint.h>
 int
 main ()
 {
-PG_INT64_TYPE val = 0;
-   PG_INT64_TYPE expect = 0;
+int64_t val = 0;
+   int64_t expect = 0;
    __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);
   ;
   return 0;
@@ -17278,13 +17115,14 @@ else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <immintrin.h>
+    #include <stdint.h>
     #if defined(__has_attribute) && __has_attribute (target)
     __attribute__((target("avx512vpopcntdq,avx512bw")))
     #endif
     static int popcount_test(void)
     {
       const char buf[sizeof(__m512i)];
-      PG_INT64_TYPE popcnt = 0;
+      int64_t popcnt = 0;
       __m512i accum = _mm512_setzero_si512();
       const __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);
       const __m512i cnt = _mm512_popcnt_epi64(val);
@@ -18954,9 +18792,6 @@ fi
 ac_config_headers="$ac_config_headers src/include/pg_config.h"

@@ -19671,7 +19506,6 @@ do
 
@@ -20279,10 +20113,6 @@ $as_echo "$as_me: WARNING: *** link for $FILE -- please fix by hand" >&2;}
 
diff --git a/configure.ac b/configure.ac
index 21817009642..074ecc62d20 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1604,6 +1604,7 @@ PGAC_C_STATIC_ASSERT
 PGAC_C_TYPEOF
 PGAC_C_TYPES_COMPATIBLE
 PGAC_C_BUILTIN_CONSTANT_P
+PGAC_C_BUILTIN_OP_OVERFLOW
 PGAC_C_BUILTIN_UNREACHABLE
 PGAC_C_COMPUTED_GOTO
 PGAC_STRUCT_TIMEZONE
@@ -1906,54 +1907,18 @@ for the exact reason.]])],
 # Run tests below here
 # --------------------
 
 AC_CHECK_SIZEOF([long])
+AC_CHECK_SIZEOF([long long])
 
 # Determine memory alignment requirements for the basic C data types.
 
 AC_CHECK_ALIGNOF(short)
 AC_CHECK_ALIGNOF(int)
 AC_CHECK_ALIGNOF(long)
 
@@ -1977,12 +1942,11 @@ MAX_ALIGNOF=$ac_cv_alignof_double
 
-
 
@@ -2472,12 +2436,6 @@ AC_CONFIG_HEADERS([src/include/pg_config.h],
 echo >src/include/stamp-h
 ])

diff --git a/meson.build b/meson.build
index 451c3f6d851..a2e35bca077 100644
--- a/meson.build
+++ b/meson.build
@@ -1588,21 +1588,6 @@ if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
   endif
 endif
 
-sizeof_long = cc.sizeof('long', args: test_c_args)
-cdata.set('SIZEOF_LONG', sizeof_long)
-if sizeof_long == 8
-  cdata.set('HAVE_LONG_INT_64', 1)
-  pg_int64_type = 'long int'
-  cdata.set_quoted('INT64_MODIFIER', 'l')
-elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
-  cdata.set('HAVE_LONG_LONG_INT_64', 1)
-  pg_int64_type = 'long long int'
-  cdata.set_quoted('INT64_MODIFIER', 'll')
-else
-  error('do not know how to get a 64bit int')
-endif
-cdata.set('PG_INT64_TYPE', pg_int64_type)
-
 if host_machine.endian() == 'big'
   cdata.set('WORDS_BIGENDIAN', 1)
 endif
@@ -1626,16 +1611,21 @@ endforeach
 # of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
 # where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
 #
-# We assume without checking that int64's alignment is at least as strong
+# We assume without checking that int64_t's alignment is at least as strong
 # as long, char, short, or int.  Note that we intentionally do not consider
 # any types wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8
 # would be too much of a penalty for disk and memory space.
 alignof_double = cdata.get('ALIGNOF_DOUBLE')
-if cc.alignment(pg_int64_type, args: test_c_args) > alignof_double
-  error('alignment of int64 is greater than the alignment of double')
+if cc.alignment('int64_t', args: test_c_args, prefix: '#include <stdint.h>') > alignof_double
+  error('alignment of int64_t is greater than the alignment of double')
 endif
 cdata.set('MAXIMUM_ALIGNOF', alignof_double)
 
+cdata.set('SIZEOF_LONG', cc.sizeof('long', args: test_c_args))
+cdata.set('SIZEOF_LONG_LONG', cc.sizeof('long long', args: test_c_args))
+
+# Check if __int128 is a working 128 bit integer type, and if so
+# define PG_INT128_TYPE to that typename.
 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
 
@@ -1834,17 +1824,17 @@ endif
 # compile, and store the results in global variables so the compiler doesn't
 # optimize away the call.
 if cc.links('''
-    INT64 a = 1;
-    INT64 b = 1;
-    INT64 result;
+    #include <stdint.h>
+    int64_t a = 1;
+    int64_t b = 1;
+    int64_t result;
 
     int main(void)
     {
 
@@ -2134,7 +2124,7 @@ int main(void)
   cdata.set(check['name'],
     cc.links(test,
       name: check['desc'],
-      args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
+      args: test_c_args) ? 1 : false
   )
 endforeach
 
@@ -2172,6 +2162,7 @@ if host_cpu == 'x86_64'
 
   prog = '''
 #include <immintrin.h>
+#include <stdint.h>
 
 #if defined(__has_attribute) && __has_attribute (target)
 __attribute__((target("avx512vpopcntdq,avx512bw")))
@@ -2179,7 +2170,7 @@ __attribute__((target("avx512vpopcntdq,avx512bw")))
 int main(void)
 {
     const char buf[sizeof(__m512i)];
-    INT64 popcnt = 0;
+    int64_t popcnt = 0;
     __m512i accum = _mm512_setzero_si512();
     const __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);
     const __m512i cnt = _mm512_popcnt_epi64(val);
@@ -2190,8 +2181,7 @@ int main(void)
 }
 '''
 
-  if cc.links(prog, name: 'AVX-512 popcount',
-        args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))])
+  if cc.links(prog, name: 'AVX-512 popcount', args: test_c_args)
     cdata.set('USE_AVX512_POPCNT_WITH_RUNTIME_CHECK', 1)
   endif
 
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 0f38d712d15..eac3d001211 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -840,12 +840,6 @@ $(top_builddir)/src/include/pg_config.h: $(top_builddir)/src/include/stamp-h ;
 $(top_builddir)/src/include/stamp-h: $(top_srcdir)/src/include/pg_config.h.in $(top_builddir)/config.status
 	cd $(top_builddir) && ./config.status src/include/pg_config.h
 
-# Also remake pg_config_ext.h from pg_config_ext.h.in, same logic as above.
-$(top_builddir)/src/include/pg_config_ext.h: $(top_builddir)/src/include/stamp-ext-h ;
-
-$(top_builddir)/src/include/stamp-ext-h: $(top_srcdir)/src/include/pg_config_ext.h.in $(top_builddir)/config.status
-	cd $(top_builddir) && ./config.status src/include/pg_config_ext.h
-
 # Also remake ecpg_config.h from ecpg_config.h.in if the latter changed, same
 # logic as above.
 $(top_builddir)/src/interfaces/ecpg/include/ecpg_config.h: $(top_builddir)/src/interfaces/ecpg/include/stamp-h ;
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index 1f9403fc5cf..c4b04d62965 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -610,10 +610,10 @@ XLogDumpStatsRow(const char *name,
 		tot_len_pct = 100 * (double) tot_len / total_len;
 
 	printf("%-27s "
-		   "%20" INT64_MODIFIER "u (%6.02f) "
-		   "%20" INT64_MODIFIER "u (%6.02f) "
-		   "%20" INT64_MODIFIER "u (%6.02f) "
-		   "%20" INT64_MODIFIER "u (%6.02f)\n",
+		   "%20" PRIu64 " (%6.02f) "
+		   "%20" PRIu64 " (%6.02f) "
+		   "%20" PRIu64 " (%6.02f) "
+		   "%20" PRIu64 " (%6.02f)\n",
 		   name, n, n_pct, rec_len, rec_len_pct, fpi_len, fpi_len_pct,
 		   tot_len, tot_len_pct);
 }
@@ -742,10 +742,10 @@ XLogDumpDisplayStats(XLogDumpConfig *config, XLogStats *stats)
 		fpi_len_pct = 100 * (double) total_fpi_len / total_len;
 
 	printf("%-27s "
-		   "%20" INT64_MODIFIER "u %-9s"
-		   "%20" INT64_MODIFIER "u %-9s"
-		   "%20" INT64_MODIFIER "u %-9s"
-		   "%20" INT64_MODIFIER "u %-6s\n",
+		   "%20" PRIu64 " %-9s"
+		   "%20" PRIu64 " %-9s"
+		   "%20" PRIu64 " %-9s"
+		   "%20" PRIu64 " %-6s\n",
 		   "Total", stats->count, "",
 		   total_rec_len, psprintf("[%.02f%%]", rec_len_pct),
 		   total_fpi_len, psprintf("[%.02f%%]", fpi_len_pct),
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 85e7b68baa3..c4c38099c5b 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -6568,13 +6568,13 @@ printResults(StatsData *total,
 					SimpleStats *cstats = &(*commands)->stats;
 
 					if (max_tries == 1)
-						printf("   %11.3f  %10" INT64_MODIFIER "d  %s\n",
+						printf("   %11.3f  %10" PRId64 " %s\n",
 							   (cstats->count > 0) ?
 							   1000.0 * cstats->sum / cstats->count : 0.0,
 							   (*commands)->failures,
 							   (*commands)->first_line);
 					else
-						printf("   %11.3f  %10" INT64_MODIFIER "d  %10" INT64_MODIFIER "d  %s\n",
+						printf("   %11.3f  %10" PRId64 " %10" PRId64 " %s\n",
 							   (cstats->count > 0) ?
 							   1000.0 * cstats->sum / cstats->count : 0.0,
 							   (*commands)->failures,
diff --git a/src/include/.gitignore b/src/include/.gitignore
index 51819fb4ddd..6e99e82fe0c 100644
--- a/src/include/.gitignore
+++ b/src/include/.gitignore
@@ -1,5 +1,3 @@
 /stamp-h
-/stamp-ext-h
 /pg_config.h
-/pg_config_ext.h
 /pg_config_os.h
diff --git a/src/include/Makefile b/src/include/Makefile
index b8b576a4de3..26332ff9cbc 100644
--- a/src/include/Makefile
+++ b/src/include/Makefile
@@ -13,7 +13,7 @@ top_builddir = ../..
 include $(top_builddir)/src/Makefile.global

 uninstall:
 
 distclean: clean
-	rm -f pg_config.h pg_config_ext.h pg_config_os.h stamp-h stamp-ext-h
+	rm -f pg_config.h pg_config_os.h stamp-h stamp-ext-h
diff --git a/src/include/c.h b/src/include/c.h
index 304dff27e34..734626e75a8 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -48,14 +48,12 @@

 
 /* System header files that should be available everywhere in Postgres */
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -471,25 +469,15 @@ typedef void (*pg_funcptr_t) (void);
  */
 typedef char *Pointer;
 
-/*
- * intN
- *		Signed integer, EXACTLY N BITS IN SIZE,
- *		used for numerical computations and the
- *		frontend/backend protocol.
- */
-typedef signed char int8;		/* == 8 bits */
-typedef signed short int16;		/* == 16 bits */
-typedef signed int int32;		/* == 32 bits */
-
-/*
- * uintN
- *		Unsigned integer, EXACTLY N BITS IN SIZE,
- *		used for numerical computations and the
- *		frontend/backend protocol.
- */
-typedef unsigned char uint8;	/* == 8 bits */
-typedef unsigned short uint16;	/* == 16 bits */
-typedef unsigned int uint32;	/* == 32 bits */
+/* Historical names for types in <stdint.h>. */
+typedef int8_t int8;
+typedef int16_t int16;
+typedef int32_t int32;
+typedef int64_t int64;
+typedef uint8_t uint8;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
 
 /*
  * bitsN
@@ -502,30 +490,14 @@ typedef uint32 bits32;			/* >= 32 bits */
 /*
  * 64-bit integers
  */
-#ifdef HAVE_LONG_INT_64
 
 /* snprintf format strings to use for 64-bit integers */
-#define INT64_FORMAT "%" INT64_MODIFIER "d"
-#define UINT64_FORMAT "%" INT64_MODIFIER "u"
-#define INT64_HEX_FORMAT "%" INT64_MODIFIER "x"
-#define UINT64_HEX_FORMAT "%" INT64_MODIFIER "x"
+#define INT64_FORMAT "%" PRId64
+#define UINT64_FORMAT "%" PRIu64
+#define INT64_HEX_FORMAT "%" PRIx64
+#define UINT64_HEX_FORMAT "%" PRIx64
 
 /*
  * 128-bit signed and unsigned integers
@@ -554,22 +526,19 @@ typedef unsigned PG_INT128_TYPE uint128
 #endif
 #endif
 
-/*
+/* Historical names for limits in <stdint.h>. */
 
 /*
  * We now always use int64 timestamps, but keep this symbol defined for the
@@ -1272,21 +1241,25 @@ extern int	fdatasync(int fildes);
  * definition of int64.  (For the naming, compare that POSIX has
  * strtoimax()/strtoumax() which return intmax_t/uintmax_t.)
  */
-#ifdef HAVE_LONG_INT_64
+#if SIZEOF_LONG == 8
 #define strtoi64(str, endptr, base) ((int64) strtol(str, endptr, base))
 #define strtou64(str, endptr, base) ((uint64) strtoul(str, endptr, base))
-#else
+#elif SIZEOF_LONG_LONG == 8
 #define strtoi64(str, endptr, base) ((int64) strtoll(str, endptr, base))
 #define strtou64(str, endptr, base) ((uint64) strtoull(str, endptr, base))
+#else
+#error "cannot find integer type of the same size as int64_t"
 #endif
 
 /*
  * Similarly, wrappers around labs()/llabs() matching our int64.
  */
-#ifdef HAVE_LONG_INT_64
+#if SIZEOF_LONG == 8
 #define i64abs(i) labs(i)
-#else
+#elif SIZEOF_LONG_LONG == 8
 #define i64abs(i) llabs(i)
+#else
+#error "cannot find integer type of the same size as int64_t"
 #endif
 
 /*
 
index ab0f8cc2b4a..07b2f798abd 100644

 
@@ -153,8 +153,8 @@
 /* Define to 1 if you have __sync_lock_test_and_set(int *) and friends. */
 #undef HAVE_GCC__SYNC_INT32_TAS
 
-/* Define to 1 if you have __sync_val_compare_and_swap(int64 *, int64, int64).
-   */
+/* Define to 1 if you have __sync_val_compare_and_swap(int64_t *, int64_t,
+   int64_t). */
 #undef HAVE_GCC__SYNC_INT64_CAS
 
 /* Define to 1 if you have the `getauxval' function. */
@@ -265,12 +265,6 @@
 /* Define to 1 if you have the `zstd' library (-lzstd). */
 #undef HAVE_LIBZSTD

@@ -544,9 +538,6 @@
 
-/* Define to the appropriate printf length modifier for 64-bit ints. */
-#undef INT64_MODIFIER
-
 /* Define as the maximum alignment requirement of any C data type. */
 #undef MAXIMUM_ALIGNOF
 
@@ -578,9 +569,6 @@
 
-/* Define to the name of a signed 64-bit integer type. */
-#undef PG_INT64_TYPE
-
 /* Define to the name of the default PostgreSQL service principal in Kerberos
    (GSSAPI). (--with-krb-srvnam=NAME) */
 #undef PG_KRB_SRVNAM
@@ -630,6 +618,9 @@
 /* The size of `long', as computed by sizeof. */
 #undef SIZEOF_LONG
 
+/* The size of `long long', as computed by sizeof. */
+#undef SIZEOF_LONG_LONG
+
 /* The size of `off_t', as computed by sizeof. */
 #undef SIZEOF_OFF_T
 
index 4d88478c9c2..a3cad46afe9 100644
@@ -74,13 +74,13 @@ pg_leftmost_one_pos64(uint64 word)
 #ifdef HAVE__BUILTIN_CLZ
 	Assert(word != 0);
 
-#if defined(HAVE_LONG_INT_64)
+#if SIZEOF_LONG == 8
 	return 63 - __builtin_clzl(word);
-#elif defined(HAVE_LONG_LONG_INT_64)
+#elif SIZEOF_LONG_LONG == 8
 	return 63 - __builtin_clzll(word);
 #else
-#error must have a working 64-bit integer datatype
-#endif							/* HAVE_LONG_INT_64 */
+#error "cannot find integer type of the same size as uint64_t"
+#endif
 
 #elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_ARM64))
 	unsigned long result;
@@ -147,13 +147,13 @@ pg_rightmost_one_pos64(uint64 word)
 #ifdef HAVE__BUILTIN_CTZ
 	Assert(word != 0);
 
-#if defined(HAVE_LONG_INT_64)
+#if SIZEOF_LONG == 8
 	return __builtin_ctzl(word);
-#elif defined(HAVE_LONG_LONG_INT_64)
+#elif SIZEOF_LONG_LONG == 8
 	return __builtin_ctzll(word);
 #else
-#error must have a working 64-bit integer datatype
-#endif							/* HAVE_LONG_INT_64 */
+#error "cannot find integer type of the same size as uint64_t"
+#endif
 
index 240ad4e93bf..202eb049622 100644

 /*
  * Object ID is a fundamental type in Postgres.
@@ -44,7 +44,7 @@ typedef unsigned int Oid;

 /*
  * Identifiers of error message fields.  Kept here to keep common
diff --git a/src/include/utils/dsa.h b/src/include/utils/dsa.h
index 8dff964bf33..6c952a43f79 100644
--- a/src/include/utils/dsa.h
+++ b/src/include/utils/dsa.h
@@ -66,7 +66,7 @@ typedef pg_atomic_uint64 dsa_pointer_atomic;
 #define dsa_pointer_atomic_write pg_atomic_write_u64
 #define dsa_pointer_atomic_fetch_add pg_atomic_fetch_add_u64
 #define dsa_pointer_atomic_compare_exchange pg_atomic_compare_exchange_u64
-#define DSA_POINTER_FORMAT "%016" INT64_MODIFIER "x"
+#define DSA_POINTER_FORMAT "%016" PRIx64
 #endif
 
 /* Flags for dsa_allocate_extended. */
diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c
index 1d482c4fa61..1fb9860371b 100644
--- a/src/interfaces/ecpg/ecpglib/typename.c
+++ b/src/interfaces/ecpg/ecpglib/typename.c
@@ -131,11 +131,12 @@ sqlda_dynamic_type(Oid type, enum COMPAT_MODE compat)
 		case INTERVALOID:
 			return ECPGt_interval;
 		case INT8OID:
-#ifdef HAVE_LONG_LONG_INT_64
-			return ECPGt_long_long;
-#endif
-#ifdef HAVE_LONG_INT_64
+#if SIZEOF_LONG == 8
 			return ECPGt_long;
+#elif SIZEOF_LONG_LONG == 8
+			return ECPGt_long_long;
+#else
+#error "cannot find integer type of the same size as INT8OID"
 #endif
 			/* Unhandled types always return a string */
 		default:
diff --git a/src/interfaces/ecpg/include/ecpg_config.h.in b/src/interfaces/ecpg/include/ecpg_config.h.in
index 75f542f263b..4af45930b61 100644
--- a/src/interfaces/ecpg/include/ecpg_config.h.in
+++ b/src/interfaces/ecpg/include/ecpg_config.h.in
@@ -1,8 +1,8 @@
 /* Define to 1 to build client libraries as thread-safe code. */
 #define ENABLE_THREAD_SAFETY 1
 
-/* Define to 1 if `long int' works and is 64 bits. */
-#undef HAVE_LONG_INT_64
+/* The size of `long', as computed by sizeof. */
+#undef SIZEOF_LONG
 
-/* Define to 1 if `long long int' works and is 64 bits. */
-#undef HAVE_LONG_LONG_INT_64
+/* The size of `long long', as computed by sizeof. */
+#undef SIZEOF_LONG_LONG
diff --git a/src/interfaces/ecpg/include/meson.build b/src/interfaces/ecpg/include/meson.build
index a3beb3cc7be..c1a88e73fb8 100644
--- a/src/interfaces/ecpg/include/meson.build
+++ b/src/interfaces/ecpg/include/meson.build
@@ -3,8 +3,8 @@
 ecpg_inc = include_directories('.')
 
 ecpg_conf_keys = [
-  'HAVE_LONG_INT_64',
+  'SIZEOF_LONG',
+  'SIZEOF_LONG_LONG',
 ]
 
 ecpg_conf_data = configuration_data()
diff --git a/src/interfaces/ecpg/include/pgtypes_interval.h b/src/interfaces/ecpg/include/pgtypes_interval.h
index 46cfce65517..f0acbb4512a 100644
--- a/src/interfaces/ecpg/include/pgtypes_interval.h
+++ b/src/interfaces/ecpg/include/pgtypes_interval.h
@@ -3,21 +3,17 @@
 #ifndef PGTYPES_INTERVAL
 #define PGTYPES_INTERVAL
 
+#include <stdint.h>
+
 #include <ecpg_config.h>
 #include <pgtypes.h>
 
 #ifndef C_H
 
-#ifdef HAVE_LONG_INT_64
-typedef long int int64;

 typedef struct
diff --git a/src/interfaces/ecpg/include/sqltypes.h b/src/interfaces/ecpg/include/sqltypes.h
index e7cbfa47956..498840458c4 100644
--- a/src/interfaces/ecpg/include/sqltypes.h
+++ b/src/interfaces/ecpg/include/sqltypes.h
@@ -46,12 +46,14 @@
 #define SQLINTERVAL		ECPGt_interval
 #define SQLNCHAR	ECPGt_char
 #define SQLNVCHAR	ECPGt_char
-#ifdef HAVE_LONG_LONG_INT_64
+#if SIZEOF_LONG == 8
+#define SQLINT8		ECPGt_long
+#define SQLSERIAL8	ECPGt_long
+#elif SIZEOF_LONG_LONG == 8
 #define SQLINT8		ECPGt_long_long
 #define SQLSERIAL8	ECPGt_long_long
 #else
-#define SQLINT8		ECPGt_long
-#define SQLSERIAL8	ECPGt_long
+#error "cannot find integer type of the same size as SQLINT8"
 #endif
 
 #endif							/* ndef ECPG_SQLTYPES_H */
diff --git a/src/interfaces/ecpg/test/expected/compat_informix-sqlda.c b/src/interfaces/ecpg/test/expected/compat_informix-sqlda.c
index 7e19319d27f..8eebc51664e 100644
--- a/src/interfaces/ecpg/test/expected/compat_informix-sqlda.c
+++ b/src/interfaces/ecpg/test/expected/compat_informix-sqlda.c
@@ -97,12 +97,14 @@ typedef struct sqlda_struct sqlda_t;
 #define SQLINTERVAL		ECPGt_interval
 #define SQLNCHAR	ECPGt_char
 #define SQLNVCHAR	ECPGt_char
-#ifdef HAVE_LONG_LONG_INT_64
+#if SIZEOF_LONG == 8
+#define SQLINT8		ECPGt_long
+#define SQLSERIAL8	ECPGt_long
+#elif SIZEOF_LONG_LONG == 8
 #define SQLINT8		ECPGt_long_long
 #define SQLSERIAL8	ECPGt_long_long
 #else
-#define SQLINT8		ECPGt_long
-#define SQLSERIAL8	ECPGt_long
+#error "cannot find integer type of the same size as SQLINT8"
 #endif
 
 #endif							/* ndef ECPG_SQLTYPES_H */
diff --git a/src/port/pg_bitutils.c b/src/port/pg_bitutils.c
index 87f56e82b80..c8399981ee0 100644
--- a/src/port/pg_bitutils.c
+++ b/src/port/pg_bitutils.c
@@ -370,12 +370,12 @@ static inline int
 pg_popcount64_slow(uint64 word)
 {
 #ifdef HAVE__BUILTIN_POPCOUNT
-#if defined(HAVE_LONG_INT_64)
+#if SIZEOF_LONG == 8
 	return __builtin_popcountl(word);
-#elif defined(HAVE_LONG_LONG_INT_64)
+#elif SIZEOF_LONG_LONG == 8
 	return __builtin_popcountll(word);
 #else
-#error must have a working 64-bit integer datatype
+#error "cannot find integer of the same size as uint64_t"
 #endif
 #else							/* !HAVE__BUILTIN_POPCOUNT */
 	int			result = 0;
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 884f0262dd1..c21af2fb2e7 100644
--- a/src/port/snprintf.c
+++ b/src/port/snprintf.c
@@ -560,6 +560,28 @@ nextch2:
 					fmtpos = accum;
 				accum = 0;
 				goto nextch2;
+#ifdef WIN32
+			case 'I':
+				/* Windows PRI*{32,64,PTR} size */
+				if (format[0] == '3' && format[1] == '2')
+					format += 2;
+				else if (format[0] == '6' && format[1] == '4')
+				{
+					format += 2;
+					longlongflag = 1;
+				}
+				else
+				{
+#if SIZEOF_VOID_P == SIZEOF_LONG
+				longflag = 1;
+#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG
+				longlongflag = 1;
+#else
+#error "cannot find integer type of the same size as intptr_t"
+#endif
+				}
+				goto nextch2;
+#endif
 			case 'l':
 				if (longflag)
 					longlongflag = 1;
@@ -567,16 +589,12 @@ nextch2:
 					longflag = 1;
 				goto nextch2;
 			case 'z':
-#if SIZEOF_SIZE_T == 8
-#ifdef HAVE_LONG_INT_64
+#if SIZEOF_SIZE_T == SIZEOF_LONG
 				longflag = 1;
-#elif defined(HAVE_LONG_LONG_INT_64)
+#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
 				longlongflag = 1;
 #else
-#error "Don't know how to print 64bit integers"
-#endif
-#else
-				/* assume size_t is same size as int */
+#error "cannot find integer type of the same size as size_t"
 #endif
 				goto nextch2;
 			case 'h':
@@ -827,6 +845,28 @@ nextch1:
 					fmtpos = accum;
 				accum = 0;
 				goto nextch1;
+#ifdef WIN32
+			case 'I':
+				/* Windows PRI*{32,64,PTR} size */
+				if (format[0] == '3' && format[1] == '2')
+					format += 2;
+				else if (format[0] == '6' && format[1] == '4')
+				{
+					format += 2;
+					longlongflag = 1;
+				}
+				else
+				{
+#if SIZEOF_VOID_P == SIZEOF_LONG
+				longflag = 1;
+#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG
+				longlongflag = 1;
+#else
+#error "cannot find integer type of the same size as intptr_t"
+#endif
+				}
+				goto nextch1;
+#endif
 			case 'l':
 				if (longflag)
 					longlongflag = 1;
@@ -834,16 +874,12 @@ nextch1:
 					longflag = 1;
 				goto nextch1;
 			case 'z':
-#if SIZEOF_SIZE_T == 8
-#ifdef HAVE_LONG_INT_64
+#if SIZEOF_SIZE_T == SIZEOF_LONG
 				longflag = 1;
-#elif defined(HAVE_LONG_LONG_INT_64)
+#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
 				longlongflag = 1;
 #else
-#error "Don't know how to print 64bit integers"
-#endif
-#else
-				/* assume size_t is same size as int */
+#error "cannot find integer type of the same size as size_t"
 #endif
 				goto nextch1;
 			case 'h':
-- 
2.47.0

---

From: Peter Eisentraut <[email-masked]>
Date: Tue, 3 Dec 2024 14:22:56 +0100

> an optional API option, that'd be a later change.

Interesting:

i) If “long long” is specified, then the <host parameter data type> of 
HV is BIGINT.
ii) If “long” is specified, then the <host parameter data type> of HV is 
INTEGER.
iii) If “short” is specified, then the <host parameter data type> of HV 
is SMALLINT.
[...]

I suppose that makes sense.

> <inttypes.h> tokens when loading message catalogues.

Ah great, thanks for checking that.

---

From: Peter Eisentraut <[email-masked]>
Date: Tue, 3 Dec 2024 14:24:47 +0100

> it via existing [U]INT64_FORMAT in various places that would break if

This patch looks good to me.

In meson.build, this comment seems to be misplaced by accident:

In c.h, you include <inttypes.h> instead of <stdint.h>.  Is there a 
reason for that?

---

From: Thomas Munro <[email-masked]>
Date: Wed, 4 Dec 2024 16:04:24 +1300

> This patch looks good to me.

Thanks!  Pushed.  Let's see what the build farm says.

> In meson.build, this comment seems to be misplaced by accident:

Oops, fixed.

> In c.h, you include <inttypes.h> instead of <stdint.h>.  Is there a

<stdint.h> was already there for uintptr_t at least.  <inttypes.h> is
needed to be able to define:

#define INT64_FORMAT "%" PRId64

---

From: Thomas Munro <[email-masked]>
Date: Wed, 4 Dec 2024 18:58:42 +1300

> Thanks!  Pushed.  Let's see what the build farm says.

A couple of armv7 animals seemed to die in the Perl tests.  Huh.  Well
I know that Perl was sensitive to this stuff but it passed on 32 bit
CI (x86).  I will try to reproduce that on local ARM hardware...

---

From: Tom Lane <[email-masked]>
Date: Wed, 04 Dec 2024 01:19:32 -0500

> CI (x86).  I will try to reproduce that on local ARM hardware...

turaco seems unhappy because of

2024-12-04 05:37:14.831 GMT [9584:18] pg_regress/plperl_setup LOG:  statement: CREATE EXTENSION plperl;
Util.c: loadable library and perl binaries are mismatched (got first handshake key 0xa300080, needed 0xa400080)

Not clear to me what these changes would have done to trigger that.

			regards, tom lane

---

From: Tom Lane <[email-masked]>
Date: Wed, 04 Dec 2024 14:41:19 -0500

Also ... grison and turaco are emitting warnings that were
not there a couple of days ago:

 grison        | 2024-12-04 17:10:09 | reconstruct.c:701:33: warning: passing argument 2 of 'copy_file_range' from incompatible pointer type [-Wincompatible-pointer-types]
 turaco        | 2024-12-04 16:15:11 | reconstruct.c:701:61: warning: passing argument 2 of 'copy_file_range' from incompatible pointer type [-Wincompatible-pointer-types]

The code they are complaining about is from ac8110155 ("Allow using
copy_file_range in write_reconstructed_file") back in April:

            off_t        off = offsetmap[i];
            ...
                wb = copy_file_range(s->fd, &off, wfd, NULL, BLCKSZ - nwritten, 0);

Now, on my Linux box "man copy_file_range" saith

       ssize_t copy_file_range(int fd_in, loff_t *off_in,
                               int fd_out, loff_t *off_out,
                               size_t len, unsigned int flags);

So apparently, "off_t" was the same as "loff_t" before 962da900a,
but it no longer is the same on 32-bit machines.  (In any case,
if all machines that have copy_file_range define it like this,
perhaps we ought to be declaring this variable as loff_t not off_t?)

Digging a bit deeper, the full warning report is

reconstruct.c: In function "write_reconstructed_file":
reconstruct.c:701:33: warning: passing argument 2 of "copy_file_range" from incompatible pointer type [-Wincompatible-pointer-types]
     wb = copy_file_range(s->fd, &off, wfd, NULL, BLCKSZ - nwritten, 0);
                                 ^~~~
In file included from reconstruct.c:15:
/usr/include/unistd.h:1107:49: note: expected "__off64_t *" {aka "long long int *"} but argument is of type "off_t *" {aka "long int *"}
 ssize_t copy_file_range (int __infd, __off64_t *__pinoff,
                                      ~~~~~~~~~~~^~~~~~~~

Since these are 32-bit machines, "long int" is 32 bits (confirmed from
their configure results), which means "off_t" is only 32 bits, which
really sounds quite broken.  I thought it was 64 bits pretty much
everywhere nowadays.  Did 962da900a cause that?  Maybe that explains
the Perl library compatibility problems these machines are reporting?

			regards, tom lane

---

From: Thomas Munro <[email-masked]>
Date: Thu, 5 Dec 2024 10:03:02 +1300

On Thu, Dec 5, 2024 at 8:41 AM Tom Lane <[email-masked]> wrote:
> but it no longer is the same on 32-bit machines.

Right, that's really weird.

> (In any case,

Hmm.  FreeBSD just has off_t in its declaration, but off_t is always
64 bit, and I don't think it knows about loff_t.

I guess copy_file_range() didn't follow the usual plan of respecting
_FILE_OFFSET_BITS, so there's no 32 bit off_t support for it in libc,
hence they needed to invent (or leak from the kernel?) loff_t.  I
don't think we should change that, because we use AC_SYS_LARGEFILE to
find that we need -D_FILE_OFFSET_BITS=64 on 32 bit systems, so off_t
should be the same as loff_t.  If something weren't broken
somewhere...  Somehow it's getting lost?

> the Perl library compatibility problems these machines are reporting?

Right, that definitely should upset Perl, as it changes the size of
struct stat and all kinds of stuff, probably.

The affected systems are mostly running ancient OSes, and lapwing is
32 bit x86, so it's not only armv7.  Current 32 bit x86 systems are
working fine.  Maybe I should boot up a Debian 7 x86 build (my
attempts to run ancient Debian/armv7 on qemu last night failed, x86
sounds easier...)  The armv7 systems except turaco are running the
same era of Debian as lapwing, but I'm not sure what turaco is.

---

From: Tom Lane <[email-masked]>
Date: Wed, 04 Dec 2024 16:20:37 -0500

> but it no longer is the same on 32-bit machines.

OK, I see what is happening.  On platforms that need it, we define
_FILE_OFFSET_BITS as 64 in pg_config.h to ensure that off_t is
big enough.  However, 962da900a did this:

Since c.h reads postgres_ext.h first, <stdint.h> is now pulled in
before pg_config.h, and that in turn pulls in <features.h>, which
locks down the decision that off_t will be 32 bits, as well as some
other decisions we don't want.  We can NOT include any system headers
before pg_config.h; I'm surprised we're not seeing related failures on
the Solaris-en, where _LARGEFILE_SOURCE is similarly critical.

Another rather serious problem here is that we no longer provide
macro PG_INT64_TYPE, which seems rather likely to break applications
that were relying on it.  That is part of our external API, we
can't just remove it on a whim.

I think the least painful solution would be to revert the parts
of 962da900a that got rid of pg_config_ext.h and PG_INT64_TYPE.
Since PG_INT64_TYPE is a macro not a typedef, it might be okay
to #define it as int64_t even before we've read that header,
so as not to give up the principle of relying on stdint.h for the
underlying definition.

Now that I see this, I'm fairly astonished that there aren't
more problems than we've noticed.  I wonder whether it'd be
a good idea to put in a static assert somewhere about the
width of off_t, so that the next screwup of this sort will
be easier to diagnose.

			regards, tom lane

---

From: Thomas Munro <[email-masked]>
Date: Thu, 5 Dec 2024 10:44:06 +1300

> +++ b/src/include/postgres_ext.h

Ah, right.   Oops.

> can't just remove it on a whim.

I had concluded that PG_INT64_TYPE wasn't part of our external API but
pg_int64 was, based on the comment:

But yeah, I obviously hadn't considered that other interaction, and ...

> so as not to give up the principle of relying on stdint.h for the

... yeah that sounds like a plan.  Looking into it.

> width of off_t, so that the next screwup of this sort will

Right, we could assert that it hasn't changed from what configure detected.

---

From: Tom Lane <[email-masked]>
Date: Wed, 04 Dec 2024 16:50:05 -0500

> +typedef int64_t pg_int64;

Oh, hmm, maybe so.  OTOH, that typedef breaks the idea of #define'ing
PG_INT64_TYPE as int64_t.  We need this header to be readable without
any prior system headers, so I'm afraid we're all the way back to
making configure derive the name of a 64-bit type.

			regards, tom lane

---

From: Thomas Munro <[email-masked]>
Date: Thu, 5 Dec 2024 13:59:32 +1300

On Thu, Dec 5, 2024 at 10:03 AM Thomas Munro <[email-masked]> wrote:
> working fine.

I was trying to figure out how I missed this, and I think it might be
that the meson build scripts didn't port AC_SYS_LARGEFILES.  So if you
build on a 32 bit Linux system with meson (like one of CI's tasks, and
also build farm animal adder) then I think you finish up with 32 bit
off_t and no SIZEOF_OFF_T, because we don't do AC_SYS_LARGEFILES'
dance to figure out if this system needs -D_FILE_OFFSET_BITS=64 (or
other similar macros for AIX, Solaris etc).  I will look into that.

---

From: Thomas Munro <[email-masked]>
Date: Thu, 5 Dec 2024 14:06:09 +1300

> other similar macros for AIX, Solaris etc).  I will look into that.

Ahh, correction, it does define it (or else perl would have
complained), but it seems that meson magically puts it into the
compiler command line without being asked.  So it is defined without
pg_config.h being involved, and thus earlier.  Huh.

---

From: Tom Lane <[email-masked]>
Date: Wed, 04 Dec 2024 22:29:38 -0500

> pg_config.h being involved, and thus earlier.  Huh.

That does not seem great.  Compile an extension without the same
CPPFLAGS, you silently get an ABI-incompatible module.  We really
ought to be putting these ABI-critical flags into pg_config.h.
It's especially bad that this works differently between autoconf
and meson builds.

			regards, tom lane

---

From: Thomas Munro <[email-masked]>
Date: Thu, 5 Dec 2024 17:26:53 +1300

> It's especially bad that this works differently between autoconf

It makes the whole MinGW circus worse that I'd realised:

https://www.postgresql.org/message-id/flat/CA%2BhUKG%2B6ZPD_X5ADMwX2uUtXqe_wv8%2BKQ5xFeAR2zbcodjNZvw%40mail.gmail.com#ddad894fcfa9733d30a579f7eb52ebf6