From 58cecd196460d12b0257f90a47acb4f6d6912afc Mon Sep 17 00:00:00 2001
From: Matthys Strydom <strydom@amazon.com>
Date: Tue, 10 Nov 2026 03:25:47 +0000
Subject: [PATCH] Report malformed integer aggregate states as array errors

Several SQL-callable aggregate support functions take an int8[]
transition state. They used elog(ERROR) when the state did not contain
the expected two elements. This assigned ERRCODE_INTERNAL_ERROR, so a
direct call such as int8_avg(ARRAY[1]::int8[]) reported SQLSTATE XX000
for invalid user input.

Use ERRCODE_ARRAY_SUBSCRIPT_ERROR instead, consistent with other code
that rejects arrays with an invalid shape. Add a regression test for
the SQLSTATE.
---
 src/backend/utils/adt/numeric.c          | 32 ++++++++++++++++++------
 src/test/regress/expected/aggregates.out |  5 ++++
 src/test/regress/sql/aggregates.sql      |  5 ++++
 3 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 7deea2046f1..cb64feccffb 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -6476,7 +6476,9 @@ int2_avg_accum(PG_FUNCTION_ARGS)
 
 	if (ARR_HASNULL(transarray) ||
 		ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
-		elog(ERROR, "expected 2-element int8 array");
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("expected 2-element int8 array")));
 
 	transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
 
@@ -6511,7 +6513,9 @@ int4_avg_accum(PG_FUNCTION_ARGS)
 
 	if (ARR_HASNULL(transarray) ||
 		ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
-		elog(ERROR, "expected 2-element int8 array");
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("expected 2-element int8 array")));
 
 	transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
 
@@ -6543,11 +6547,15 @@ int4_avg_combine(PG_FUNCTION_ARGS)
 
 	if (ARR_HASNULL(transarray1) ||
 		ARR_SIZE(transarray1) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
-		elog(ERROR, "expected 2-element int8 array");
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("expected 2-element int8 array")));
 
 	if (ARR_HASNULL(transarray2) ||
 		ARR_SIZE(transarray2) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
-		elog(ERROR, "expected 2-element int8 array");
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("expected 2-element int8 array")));
 
 	state1 = (Int8TransTypeData *) ARR_DATA_PTR(transarray1);
 	state2 = (Int8TransTypeData *) ARR_DATA_PTR(transarray2);
@@ -6582,7 +6590,9 @@ int2_avg_accum_inv(PG_FUNCTION_ARGS)
 
 	if (ARR_HASNULL(transarray) ||
 		ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
-		elog(ERROR, "expected 2-element int8 array");
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("expected 2-element int8 array")));
 
 	transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
 	transdata->count--;
@@ -6610,7 +6620,9 @@ int4_avg_accum_inv(PG_FUNCTION_ARGS)
 
 	if (ARR_HASNULL(transarray) ||
 		ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
-		elog(ERROR, "expected 2-element int8 array");
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("expected 2-element int8 array")));
 
 	transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
 	transdata->count--;
@@ -6629,7 +6641,9 @@ int8_avg(PG_FUNCTION_ARGS)
 
 	if (ARR_HASNULL(transarray) ||
 		ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
-		elog(ERROR, "expected 2-element int8 array");
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("expected 2-element int8 array")));
 	transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
 
 	/* SQL defines AVG of no values to be NULL */
@@ -6654,7 +6668,9 @@ int2int4_sum(PG_FUNCTION_ARGS)
 
 	if (ARR_HASNULL(transarray) ||
 		ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
-		elog(ERROR, "expected 2-element int8 array");
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("expected 2-element int8 array")));
 	transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
 
 	/* SQL defines SUM of no values to be NULL */
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index ce555a31693..3d67040c7ee 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -25,6 +25,11 @@ SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100;
  32.6666666666666667
 (1 row)
 
+-- verify malformed aggregate transition states use a data exception SQLSTATE
+\set VERBOSITY sqlstate
+SELECT int8_avg(ARRAY[1]::int8[]);
+ERROR:  2202E
+\set VERBOSITY default
 SELECT any_value(v) FROM (VALUES (1), (2), (3)) AS v (v);
  any_value 
 -----------
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 0936088b074..4d9d294da51 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -24,6 +24,11 @@ SELECT avg(four) AS avg_1 FROM onek;
 
 SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100;
 
+-- verify malformed aggregate transition states use a data exception SQLSTATE
+\set VERBOSITY sqlstate
+SELECT int8_avg(ARRAY[1]::int8[]);
+\set VERBOSITY default
+
 SELECT any_value(v) FROM (VALUES (1), (2), (3)) AS v (v);
 SELECT any_value(v) FROM (VALUES (NULL)) AS v (v);
 SELECT any_value(v) FROM (VALUES (NULL), (1), (2)) AS v (v);
-- 
2.50.1

