more C99 cleanup
I have been hunting down the last few pieces of code that made some
obsolete claims about not being able to use C99 yet or needing to be
compatible with pre-C99 or something like that. I found two obsolete
comments and two places where we could now actually use the C99
functionality that the comment was anticipating.
At least according to my grepping, all the remaining mentions of "C99"
are now up to date and relevant.
Attachments:
0001-Replace-internal-C-function-pg_hypot-by-standard-hyp.patchtext/plain; charset=UTF-8; name=0001-Replace-internal-C-function-pg_hypot-by-standard-hyp.patchDownload
From 6ba9a93498de3f73c14706463c6ed2c47577f223 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Sun, 2 Nov 2025 21:12:53 +0100
Subject: [PATCH 1/4] Replace internal C function pg_hypot() by standard
hypot()
The code comment said, "It is expected that this routine will
eventually be replaced with the C99 hypot() function.", so let's do
that now.
---
src/backend/access/spgist/spgproc.c | 2 +-
src/backend/utils/adt/geo_ops.c | 76 ++---------------------------
src/backend/utils/adt/geo_spgist.c | 2 +-
src/include/utils/geo_decls.h | 8 ---
4 files changed, 6 insertions(+), 82 deletions(-)
diff --git a/src/backend/access/spgist/spgproc.c b/src/backend/access/spgist/spgproc.c
index 660009291da..722c17ce2e5 100644
--- a/src/backend/access/spgist/spgproc.c
+++ b/src/backend/access/spgist/spgproc.c
@@ -51,7 +51,7 @@ point_box_distance(Point *point, BOX *box)
else
dy = 0.0;
- return HYPOT(dx, dy);
+ return hypot(dx, dy);
}
/*
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 377a1b3f3ad..9101a720744 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -1276,7 +1276,7 @@ line_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(float8_div(fabs(float8_mi(l1->C,
float8_mul(ratio, l2->C))),
- HYPOT(l1->A, l1->B)));
+ hypot(l1->A, l1->B)));
}
/* line_interpt()
@@ -2001,7 +2001,7 @@ point_distance(PG_FUNCTION_ARGS)
static inline float8
point_dt(Point *pt1, Point *pt2)
{
- return HYPOT(float8_mi(pt1->x, pt2->x), float8_mi(pt1->y, pt2->y));
+ return hypot(float8_mi(pt1->x, pt2->x), float8_mi(pt1->y, pt2->y));
}
Datum
@@ -5005,7 +5005,7 @@ circle_mul_pt(PG_FUNCTION_ARGS)
result = (CIRCLE *) palloc(sizeof(CIRCLE));
point_mul_point(&result->center, &circle->center, point);
- result->radius = float8_mul(circle->radius, HYPOT(point->x, point->y));
+ result->radius = float8_mul(circle->radius, hypot(point->x, point->y));
PG_RETURN_CIRCLE_P(result);
}
@@ -5020,7 +5020,7 @@ circle_div_pt(PG_FUNCTION_ARGS)
result = (CIRCLE *) palloc(sizeof(CIRCLE));
point_div_point(&result->center, &circle->center, point);
- result->radius = float8_div(circle->radius, HYPOT(point->x, point->y));
+ result->radius = float8_div(circle->radius, hypot(point->x, point->y));
PG_RETURN_CIRCLE_P(result);
}
@@ -5492,71 +5492,3 @@ plist_same(int npts, Point *p1, Point *p2)
return false;
}
-
-
-/*-------------------------------------------------------------------------
- * Determine the hypotenuse.
- *
- * If required, x and y are swapped to make x the larger number. The
- * traditional formula of x^2+y^2 is rearranged to factor x outside the
- * sqrt. This allows computation of the hypotenuse for significantly
- * larger values, and with a higher precision than when using the naive
- * formula. In particular, this cannot overflow unless the final result
- * would be out-of-range.
- *
- * sqrt( x^2 + y^2 ) = sqrt( x^2( 1 + y^2/x^2) )
- * = x * sqrt( 1 + y^2/x^2 )
- * = x * sqrt( 1 + y/x * y/x )
- *
- * It is expected that this routine will eventually be replaced with the
- * C99 hypot() function.
- *
- * This implementation conforms to IEEE Std 1003.1 and GLIBC, in that the
- * case of hypot(inf,nan) results in INF, and not NAN.
- *-----------------------------------------------------------------------
- */
-float8
-pg_hypot(float8 x, float8 y)
-{
- float8 yx,
- result;
-
- /* Handle INF and NaN properly */
- if (isinf(x) || isinf(y))
- return get_float8_infinity();
-
- if (isnan(x) || isnan(y))
- return get_float8_nan();
-
- /* Else, drop any minus signs */
- x = fabs(x);
- y = fabs(y);
-
- /* Swap x and y if needed to make x the larger one */
- if (x < y)
- {
- float8 temp = x;
-
- x = y;
- y = temp;
- }
-
- /*
- * If y is zero, the hypotenuse is x. This test saves a few cycles in
- * such cases, but more importantly it also protects against
- * divide-by-zero errors, since now x >= y.
- */
- if (y == 0.0)
- return x;
-
- /* Determine the hypotenuse */
- yx = y / x;
- result = x * sqrt(1.0 + (yx * yx));
-
- if (unlikely(isinf(result)))
- float_overflow_error();
- if (unlikely(result == 0.0))
- float_underflow_error();
-
- return result;
-}
diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index fec33e95372..4308a3065cd 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -390,7 +390,7 @@ pointToRectBoxDistance(Point *point, RectBox *rect_box)
else
dy = 0;
- return HYPOT(dx, dy);
+ return hypot(dx, dy);
}
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 8a9df75c93c..d4f7ea31f62 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -88,7 +88,6 @@ FPge(double A, double B)
#define FPge(A,B) ((A) >= (B))
#endif
-#define HYPOT(A, B) pg_hypot(A, B)
/*---------------------------------------------------------------------
* Point - (x,y)
@@ -275,11 +274,4 @@ CirclePGetDatum(const CIRCLE *X)
#define PG_GETARG_CIRCLE_P(n) DatumGetCircleP(PG_GETARG_DATUM(n))
#define PG_RETURN_CIRCLE_P(x) return CirclePGetDatum(x)
-
-/*
- * in geo_ops.c
- */
-
-extern float8 pg_hypot(float8 x, float8 y);
-
#endif /* GEO_DECLS_H */
--
2.51.0
0002-Update-comment-related-to-C99.patchtext/plain; charset=UTF-8; name=0002-Update-comment-related-to-C99.patchDownload
From 5eff322218b1bfa578686fd648592037640fa5a7 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Sun, 2 Nov 2025 21:14:12 +0100
Subject: [PATCH 2/4] Update comment related to C99
---
src/bin/pg_dump/dumputils.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h
index 10f6e27c0a0..57d36f386f0 100644
--- a/src/bin/pg_dump/dumputils.h
+++ b/src/bin/pg_dump/dumputils.h
@@ -26,8 +26,7 @@
* localized, which means they may contain characters in various random
* encodings; this has been seen to cause encoding errors when reading the
* dump script. Think not to get around that by using %z, because
- * (1) %z is not portable to pre-C99 systems, and
- * (2) %z doesn't actually act differently from %Z on Windows anyway.
+ * %z doesn't actually act differently from %Z on Windows.
*/
#ifndef WIN32
#define PGDUMP_STRFTIME_FMT "%Y-%m-%d %H:%M:%S %Z"
--
2.51.0
0003-Fix-pg_isblank.patchtext/plain; charset=UTF-8; name=0003-Fix-pg_isblank.patchDownload
From cd7e6fa5d2e0145bf961be55b3aed25faa3d7637 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Fri, 21 Nov 2025 13:59:28 +0100
Subject: [PATCH 3/4] Fix pg_isblank()
There was a pg_isblank() function that claimed to be a replacement for
the standard isblank() function, which was thought to be "not very
portable yet". We can now assume that it's portable (it's in C99).
But pg_isblank() actually diverged from the standard isblank() by also
accepting '\r', while the standard one only accepts space and tab.
This was added to support parsing pg_hba.conf under Windows. But the
hba parsing code now works completely differently and already handles
line endings before we get to pg_isblank(). The other user of
pg_isblank() is for ident protocol message parsing, which also handles
'\r' separately. So this behavior is now obsolete and confusing.
To improve clarity, I separated those concerns. The ident parsing now
gets its own function that hardcodes the whitespace characters
mentioned by the relevant RFC. The hba parsing uses the standard
isblank(). pg_isblank() is removed.
---
src/backend/libpq/auth.c | 17 +++++++++++++----
src/backend/libpq/hba.c | 15 ++-------------
src/include/libpq/hba.h | 1 -
3 files changed, 15 insertions(+), 18 deletions(-)
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index ec4dbacf015..5854a2433bb 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -1580,6 +1580,15 @@ pg_SSPI_make_upn(char *accountname,
*----------------------------------------------------------------
*/
+/*
+ * Per RFC 1413, space and tab are whitespace in ident messages.
+ */
+static bool
+is_ident_whitespace(const char c)
+{
+ return c == ' ' || c == '\t';
+}
+
/*
* Parse the string "*ident_response" as a response from a query to an Ident
* server. If it's a normal response indicating a user name, return true
@@ -1613,14 +1622,14 @@ interpret_ident_response(const char *ident_response,
int i; /* Index into *response_type */
cursor++; /* Go over colon */
- while (pg_isblank(*cursor))
+ while (is_ident_whitespace(*cursor))
cursor++; /* skip blanks */
i = 0;
- while (*cursor != ':' && *cursor != '\r' && !pg_isblank(*cursor) &&
+ while (*cursor != ':' && *cursor != '\r' && !is_ident_whitespace(*cursor) &&
i < (int) (sizeof(response_type) - 1))
response_type[i++] = *cursor++;
response_type[i] = '\0';
- while (pg_isblank(*cursor))
+ while (is_ident_whitespace(*cursor))
cursor++; /* skip blanks */
if (strcmp(response_type, "USERID") != 0)
return false;
@@ -1643,7 +1652,7 @@ interpret_ident_response(const char *ident_response,
else
{
cursor++; /* Go over colon */
- while (pg_isblank(*cursor))
+ while (is_ident_whitespace(*cursor))
cursor++; /* skip blanks */
/* Rest of line is user name. Copy it over. */
i = 0;
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 97a3586000b..787aee49d24 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -138,17 +138,6 @@ static int regexec_auth_token(const char *match, AuthToken *token,
static void tokenize_error_callback(void *arg);
-/*
- * isblank() exists in the ISO C99 spec, but it's not very portable yet,
- * so provide our own version.
- */
-bool
-pg_isblank(const char c)
-{
- return c == ' ' || c == '\t' || c == '\r';
-}
-
-
/*
* Grab one token out of the string pointed to by *lineptr.
*
@@ -198,7 +187,7 @@ next_token(char **lineptr, StringInfo buf,
*terminating_comma = false;
/* Move over any whitespace and commas preceding the next token */
- while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
+ while ((c = (*(*lineptr)++)) != '\0' && (isblank((unsigned char) c) || c == ','))
;
/*
@@ -206,7 +195,7 @@ next_token(char **lineptr, StringInfo buf,
* unquoted whitespace.
*/
while (c != '\0' &&
- (!pg_isblank(c) || in_quote))
+ (!isblank((unsigned char) c) || in_quote))
{
/* skip comments to EOL */
if (c == '#' && !in_quote)
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index e3748d3c8c9..7b93ba4a709 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -181,7 +181,6 @@ extern int check_usermap(const char *usermap_name,
bool case_insensitive);
extern HbaLine *parse_hba_line(TokenizedAuthLine *tok_line, int elevel);
extern IdentLine *parse_ident_line(TokenizedAuthLine *tok_line, int elevel);
-extern bool pg_isblank(const char c);
extern FILE *open_auth_file(const char *filename, int elevel, int depth,
char **err_msg);
extern void free_auth_file(FILE *file, int depth);
--
2.51.0
0004-Remove-obsolete-comment.patchtext/plain; charset=UTF-8; name=0004-Remove-obsolete-comment.patchDownload
From be5395ae0ecbadcaa0015064b0f7325236211c33 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Fri, 21 Nov 2025 14:02:39 +0100
Subject: [PATCH 4/4] Remove obsolete comment
This comment should probably have been moved to pg_locale_libc.c in
commit 66ac94cdc79 (2024), but upon closer examination it was already
completely obsolete then.
The first part of the comment has been obsolete since commit
85feb77aa09 (2017), which required that the system provides the
required wide-character functions.
The second part has been obsolete since commit e9931bfb751 (2024),
which eliminated code paths depending on the global LC_CTYPE setting.
---
src/backend/utils/adt/formatting.c | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 5f7b3114da7..5bfeda2ffde 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -1616,16 +1616,6 @@ str_numth(char *dest, const char *num, enum TH_Case type)
* upper/lower/initcap functions
*****************************************************************************/
-/*
- * If the system provides the needed functions for wide-character manipulation
- * (which are all standardized by C99), then we implement upper/lower/initcap
- * using wide-character functions, if necessary. Otherwise we use the
- * traditional <ctype.h> functions, which of course will not work as desired
- * in multibyte character sets. Note that in either case we are effectively
- * assuming that the database character encoding matches the encoding implied
- * by LC_CTYPE.
- */
-
/*
* collation-aware, wide-character-aware lower function
*
--
2.51.0
On Sat, Nov 22, 2025 at 2:50 AM Peter Eisentraut <peter@eisentraut.org> wrote:
+ * %z doesn't actually act differently from %Z on Windows.
Probably obsolete too, at least according to:
Peter Eisentraut <peter@eisentraut.org> writes:
I have been hunting down the last few pieces of code that made some
obsolete claims about not being able to use C99 yet or needing to be
compatible with pre-C99 or something like that. I found two obsolete
comments and two places where we could now actually use the C99
functionality that the comment was anticipating.
Two comments:
* In 0001,
- * This implementation conforms to IEEE Std 1003.1 and GLIBC, in that the
- * case of hypot(inf,nan) results in INF, and not NAN.
I have a distinct recollection that this comment exists because we
found that some platforms had a hypot() that got that edge case wrong.
I don't object to proceeding on the assumption that they all conform
to spec by now, but please make sure there's at least one regression
test that will expose the problem if someplace doesn't. (A quick check
would be to hot-wire pg_hypot to do the wrong thing and see if any
existing test falls over. I think there is one, but let's verify.)
* In 0003, we can't be very sure what "isblank((unsigned char) c)"
will do with non-ASCII input data, except that it could very
easily do insane things with fragments of a multibyte character.
I recommend keeping pg_isblank but redefining it along the lines of
bool
pg_isblank(unsigned char c)
{
return (!IS_HIGHBIT_SET(c) && isblank(c));
}
to ensure the previous treatment of non-ASCII data.
(It could be made static in hba.c, perhaps)
regards, tom lane
I wrote:
I have a distinct recollection that this comment exists because we
found that some platforms had a hypot() that got that edge case wrong.
I don't object to proceeding on the assumption that they all conform
to spec by now, but please make sure there's at least one regression
test that will expose the problem if someplace doesn't. (A quick check
would be to hot-wire pg_hypot to do the wrong thing and see if any
existing test falls over. I think there is one, but let's verify.)
Ah, there are several. It's not totally obvious perhaps where the
cause is. I'll attach the diffs just for the archives' sake.
regards, tom lane
Attachments:
regression.diffstext/x-diff; charset=us-ascii; name=regression.diffsDownload
diff -U3 /home/postgres/pgsql/src/test/regress/expected/geometry.out /home/postgres/pgsql/src/test/regress/results/geometry.out
--- /home/postgres/pgsql/src/test/regress/expected/geometry.out 2025-09-16 11:41:22.033540812 -0400
+++ /home/postgres/pgsql/src/test/regress/results/geometry.out 2025-11-21 11:07:33.559382194 -0500
@@ -555,9 +555,9 @@
(1e+300,Infinity) | {0,-1,5} | Infinity | Infinity
(1e+300,Infinity) | {1,0,5} | NaN | NaN
(1e+300,Infinity) | {0,3,0} | Infinity | Infinity
- (1e+300,Infinity) | {1,-1,0} | Infinity | Infinity
- (1e+300,Infinity) | {-0.4,-1,-6} | Infinity | Infinity
- (1e+300,Infinity) | {-0.000184615384615,-1,15.3846153846} | Infinity | Infinity
+ (1e+300,Infinity) | {1,-1,0} | NaN | NaN
+ (1e+300,Infinity) | {-0.4,-1,-6} | NaN | NaN
+ (1e+300,Infinity) | {-0.000184615384615,-1,15.3846153846} | NaN | NaN
(1e+300,Infinity) | {3,NaN,5} | NaN | NaN
(1e+300,Infinity) | {NaN,NaN,NaN} | NaN | NaN
(1e+300,Infinity) | {0,-1,3} | Infinity | Infinity
@@ -653,7 +653,7 @@
(1e+300,Infinity) | [(11,22),(33,44)] | Infinity | Infinity
(1e+300,Infinity) | [(-10,2),(-10,3)] | Infinity | Infinity
(1e+300,Infinity) | [(0,-20),(30,-20)] | Infinity | Infinity
- (1e+300,Infinity) | [(NaN,1),(NaN,90)] | Infinity | Infinity
+ (1e+300,Infinity) | [(NaN,1),(NaN,90)] | NaN | NaN
(Infinity,1e+300) | [(1,2),(3,4)] | Infinity | Infinity
(Infinity,1e+300) | [(0,0),(6,6)] | Infinity | Infinity
(Infinity,1e+300) | [(10,-10),(-3,-4)] | Infinity | Infinity
@@ -1070,9 +1070,9 @@
(1e+300,Infinity) | {0,-1,5} | (1e+300,5)
(1e+300,Infinity) | {1,0,5} |
(1e+300,Infinity) | {0,3,0} | (1e+300,0)
- (1e+300,Infinity) | {1,-1,0} | (Infinity,NaN)
- (1e+300,Infinity) | {-0.4,-1,-6} | (-Infinity,NaN)
- (1e+300,Infinity) | {-0.000184615384615,-1,15.3846153846} | (-Infinity,NaN)
+ (1e+300,Infinity) | {1,-1,0} |
+ (1e+300,Infinity) | {-0.4,-1,-6} |
+ (1e+300,Infinity) | {-0.000184615384615,-1,15.3846153846} |
(1e+300,Infinity) | {3,NaN,5} |
(1e+300,Infinity) | {NaN,NaN,NaN} |
(1e+300,Infinity) | {0,-1,3} | (1e+300,3)
@@ -1168,7 +1168,7 @@
(1e+300,Infinity) | [(11,22),(33,44)] | (33,44)
(1e+300,Infinity) | [(-10,2),(-10,3)] | (-10,3)
(1e+300,Infinity) | [(0,-20),(30,-20)] | (30,-20)
- (1e+300,Infinity) | [(NaN,1),(NaN,90)] | (NaN,90)
+ (1e+300,Infinity) | [(NaN,1),(NaN,90)] |
(Infinity,1e+300) | [(1,2),(3,4)] | (3,4)
(Infinity,1e+300) | [(0,0),(6,6)] | (6,6)
(Infinity,1e+300) | [(10,-10),(-3,-4)] | (-3,-4)
On 21.11.25 15:03, Thomas Munro wrote:
On Sat, Nov 22, 2025 at 2:50 AM Peter Eisentraut <peter@eisentraut.org> wrote:
+ * %z doesn't actually act differently from %Z on Windows.Probably obsolete too, at least according to:
Right, we could got back to using %z on all platforms, as originally
attempted by commit ad5d46a4494.
On 21.11.25 17:10, Tom Lane wrote:
I wrote:
I have a distinct recollection that this comment exists because we
found that some platforms had a hypot() that got that edge case wrong.
I don't object to proceeding on the assumption that they all conform
to spec by now, but please make sure there's at least one regression
test that will expose the problem if someplace doesn't. (A quick check
would be to hot-wire pg_hypot to do the wrong thing and see if any
existing test falls over. I think there is one, but let's verify.)Ah, there are several. It's not totally obvious perhaps where the
cause is. I'll attach the diffs just for the archives' sake.
For clarification, does what you are showing mean that the regression
tests have enough coverage of the hypot() edge cases?
Peter Eisentraut <peter@eisentraut.org> writes:
On 21.11.25 17:10, Tom Lane wrote:
Ah, there are several. It's not totally obvious perhaps where the
cause is. I'll attach the diffs just for the archives' sake.
For clarification, does what you are showing mean that the regression
tests have enough coverage of the hypot() edge cases?
We are definitely covering it. It's just that the present tests
fail in a way that wouldn't scream "hypot is broken" to some
future hacker dealing with such a failure. Not sure if that's
worth worrying about; but I'd lean to not worrying unless you
commit and we see such failures in the buildfarm.
regards, tom lane
On 21.11.25 16:35, Tom Lane wrote:
* In 0003, we can't be very sure what "isblank((unsigned char) c)"
will do with non-ASCII input data, except that it could very
easily do insane things with fragments of a multibyte character.
I recommend keeping pg_isblank but redefining it along the lines ofbool
pg_isblank(unsigned char c)
{
return (!IS_HIGHBIT_SET(c) && isblank(c));
}to ensure the previous treatment of non-ASCII data.
(It could be made static in hba.c, perhaps)
Ok, done that way.
On 25.11.25 06:45, Peter Eisentraut wrote:
On 21.11.25 15:03, Thomas Munro wrote:
On Sat, Nov 22, 2025 at 2:50 AM Peter Eisentraut
<peter@eisentraut.org> wrote:
+ * %z doesn't actually act differently from %Z on Windows.Probably obsolete too, at least according to:
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/
strftime-wcsftime-strftime-l-wcsftime-l?view=msvc-170Right, we could got back to using %z on all platforms, as originally
attempted by commit ad5d46a4494.
I decided not to go further into researching and/or changing this, so I
just updated the comment to indicate that the previously stated problems
might now be obsolete.
With that, all the patches mentioned in this thread have been committed.