From 26f9012beecf36bcffc5a2081c7e698d3ecf6aa2 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Mon, 30 Mar 2026 09:06:27 +0200 Subject: [PATCH] Make cast function from circle to polygon error safe Previously, the function casting type circle to type polygon could not be made error safe, because it is an SQL language function. This refactors it as a C/internal function, by sharing code with the C/internal function that the SQL function previously wrapped, and soft error support is added. Author: jian he Reviewed-by: Amul Sul Reviewed-by: Corey Huinker Discussion: Discussion: https://www.postgresql.org/message-id/flat/CADkLM%3Dfv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ%40mail.gmail.com --- src/backend/catalog/system_functions.sql | 6 --- src/backend/utils/adt/geo_ops.c | 59 ++++++++++++++++++------ src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.dat | 4 +- 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql index 1c5b6d6df05..c3c0a6e84ed 100644 --- a/src/backend/catalog/system_functions.sql +++ b/src/backend/catalog/system_functions.sql @@ -99,12 +99,6 @@ CREATE OR REPLACE FUNCTION path_contain_pt(path, point) IMMUTABLE PARALLEL SAFE STRICT COST 1 RETURN on_ppath($2, $1); -CREATE OR REPLACE FUNCTION polygon(circle) - RETURNS polygon - LANGUAGE sql - IMMUTABLE PARALLEL SAFE STRICT COST 1 -RETURN polygon(12, $1); - CREATE OR REPLACE FUNCTION age(timestamptz) RETURNS interval LANGUAGE sql diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index cbc4621e9b2..cc5ce013d0f 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -125,6 +125,7 @@ static float8 circle_ar(CIRCLE *circle); /* Routines for polygons */ static void make_bound_box(POLYGON *poly); +static POLYGON *circle_poly_internal(int32 npts, const CIRCLE *circle, FunctionCallInfo fcinfo); static void poly_to_circle(CIRCLE *result, POLYGON *poly, Node *escontext); static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start); static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly); @@ -5317,11 +5318,9 @@ fail: } -Datum -circle_poly(PG_FUNCTION_ARGS) +static POLYGON * +circle_poly_internal(int32 npts, const CIRCLE *circle, FunctionCallInfo fcinfo) { - int32 npts = PG_GETARG_INT32(0); - CIRCLE *circle = PG_GETARG_CIRCLE_P(1); POLYGON *poly; int base_size, size; @@ -5330,12 +5329,12 @@ circle_poly(PG_FUNCTION_ARGS) float8 anglestep; if (FPzero(circle->radius)) - ereport(ERROR, + ereturn(fcinfo->context, NULL, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert circle with radius zero to polygon"))); if (npts < 2) - ereport(ERROR, + ereturn(fcinfo->context, NULL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("must request at least 2 points"))); @@ -5344,7 +5343,7 @@ circle_poly(PG_FUNCTION_ARGS) /* Check for integer overflow */ if (base_size / npts != sizeof(poly->p[0]) || size <= base_size) - ereport(ERROR, + ereturn(fcinfo->context, NULL, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("too many points requested"))); @@ -5356,17 +5355,51 @@ circle_poly(PG_FUNCTION_ARGS) for (i = 0; i < npts; i++) { - angle = float8_mul(anglestep, i); + float8 temp; - poly->p[i].x = float8_mi(circle->center.x, - float8_mul(circle->radius, cos(angle))); - poly->p[i].y = float8_pl(circle->center.y, - float8_mul(circle->radius, sin(angle))); + angle = float8_mul_safe(anglestep, i, fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + return NULL; + + temp = float8_mul_safe(circle->radius, cos(angle), fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + return NULL; + + poly->p[i].x = float8_mi_safe(circle->center.x, temp, fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + return NULL; + + temp = float8_mul_safe(circle->radius, sin(angle), fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + return NULL; + + poly->p[i].y = float8_pl_safe(circle->center.y, temp, fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + return NULL; } make_bound_box(poly); - PG_RETURN_POLYGON_P(poly); + return poly; +} + +Datum +circle_poly(PG_FUNCTION_ARGS) +{ + int32 npts = PG_GETARG_INT32(0); + CIRCLE *circle = PG_GETARG_CIRCLE_P(1); + + PG_RETURN_POLYGON_P(circle_poly_internal(npts, circle, fcinfo)); +} + +/* convert circle to 12-vertex polygon */ +Datum +circle_to_poly(PG_FUNCTION_ARGS) +{ + int32 npts = 12; + CIRCLE *circle = PG_GETARG_CIRCLE_P(0); + + PG_RETURN_POLYGON_P(circle_poly_internal(npts, circle, fcinfo)); } /* diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 4d9ccc5789c..81b2bf39b3f 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202603241 +#define CATALOG_VERSION_NO 202603301 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 0118e970dda..3579cec5744 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -3390,8 +3390,8 @@ proname => 'center', prorettype => 'point', proargtypes => 'circle', prosrc => 'circle_center' }, { oid => '1544', descr => 'convert circle to 12-vertex polygon', - proname => 'polygon', prolang => 'sql', prorettype => 'polygon', - proargtypes => 'circle', prosrc => 'see system_functions.sql' }, + proname => 'polygon', prorettype => 'polygon', + proargtypes => 'circle', prosrc => 'circle_to_poly' }, { oid => '1545', descr => 'number of points', proname => 'npoints', prorettype => 'int4', proargtypes => 'path', prosrc => 'path_npoints' },