From b99e27217a0aedcc4d45ee794bb1f276f0aebaad Mon Sep 17 00:00:00 2001 From: jian he Date: Wed, 25 Mar 2026 17:19:18 +0800 Subject: [PATCH v25 7/9] error safe for casting circle to polygon Previously: Function that casting type circle to type polygon cannot be error safe, because it's a SQL language function. Now refactor it as a C/internal function, so it can error-safe. Author: jian he Reviewed-by: Amul Sul Reviewed-by: Corey Huinker Discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com Commitfest: https://commitfest.postgresql.org/patch/5941 --- src/backend/catalog/system_functions.sql | 6 --- src/backend/utils/adt/geo_ops.c | 52 ++++++++++++++++++++---- src/include/catalog/pg_proc.dat | 4 +- 3 files changed, 45 insertions(+), 17 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 a59f1cabc44..4846fe10f9c 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -147,6 +147,7 @@ static bool path_decode(char *str, bool opentype, int npts, Point *p, const char *type_name, const char *orig_string, Node *escontext); static char *path_encode(enum path_delim path_delim, int npts, Point *pt); +static Datum circle_poly_internal(int32 npts, CIRCLE *circle, FunctionCallInfo fcinfo); /* @@ -5319,26 +5320,33 @@ fail: PG_RETURN_NULL(); } - Datum circle_poly(PG_FUNCTION_ARGS) { int32 npts = PG_GETARG_INT32(0); CIRCLE *circle = PG_GETARG_CIRCLE_P(1); + + PG_RETURN_DATUM(circle_poly_internal(npts, circle, fcinfo)); +} + +Datum +circle_poly_internal(int32 npts, CIRCLE *circle, FunctionCallInfo fcinfo) +{ POLYGON *poly; int base_size, size; int i; float8 angle; float8 anglestep; + float8 temp; if (FPzero(circle->radius)) - ereport(ERROR, + ereturn(fcinfo->context, (Datum) 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert circle with radius zero to polygon"))); if (npts < 2) - ereport(ERROR, + ereturn(fcinfo->context, (Datum) 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("must request at least 2 points"))); @@ -5347,7 +5355,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, (Datum) 0, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("too many points requested"))); @@ -5359,17 +5367,43 @@ circle_poly(PG_FUNCTION_ARGS) for (i = 0; i < npts; i++) { - angle = float8_mul(anglestep, i); + angle = float8_mul_safe(anglestep, i, fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + goto fail; - 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))); + temp = float8_mul_safe(circle->radius, cos(angle), fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + goto fail; + + poly->p[i].x = float8_mi_safe(circle->center.x, temp, fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + goto fail; + + temp = float8_mul_safe(circle->radius, sin(angle), fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + goto fail; + + poly->p[i].y = float8_pl_safe(circle->center.y, temp, fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + goto fail; } make_bound_box(poly); PG_RETURN_POLYGON_P(poly); + +fail: + PG_RETURN_NULL(); +} + +/* 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_DATUM(circle_poly_internal(npts, circle, fcinfo)); } /* 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' }, -- 2.34.1