From b99e27217a0aedcc4d45ee794bb1f276f0aebaad Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
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 <jian.universality@gmail.com>
Reviewed-by: Amul Sul <sulamul@gmail.com>
Reviewed-by: Corey Huinker <corey.huinker@gmail.com>
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

