From 9012ae141def2e4698d43692acf07a8178d55233 Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huinker@gmail.com>
Date: Thu, 12 Nov 2026 13:23:26 -0500
Subject: [PATCH v19842.2 2/3] pg_dump: Map mismatched-NULL values arrays to
 empty array.

As reported in two bugs, in versions prior to v19 it is possible for
range_length_histogram to be NULL when range_empty_frac is NOT NULL and
for most_common_elems to be NULL when most_common_freqs is NOT NULL.
This is solved in v19 where the NULL values are now empty arrays, so
pg_dump must make the same mapping when exporting stats from earlier
versions.

Bug: #19842
Reported-by: chunling qin <303677365@qq.com>
Discussion: https://postgr.es/m/19842-6e808a09ff4b75d2@postgresql.org
Bug: #19857
Reported-by: Atish Prakash <125atishprak@gmail.com>
Discussion: https://postgr.es/m/19857-6e808a09ff4b75d2@postgresql.org
Author: Corey Huinker <corey.huinker@gmail.com>
Backpatch-through: 19
---
 src/bin/pg_dump/pg_dump.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index a3f38f3b886..dbb49506f83 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -11341,6 +11341,13 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 				pg_fatal("could not find index attname \"%s\"", attname);
 		}
 
+		/*
+		 * Prior to v19, range_length_histogram and most_common_elems could
+		 * have a NULL value while corresponding stanumbers field
+		 * (range_empty_frac and most_common_elem_freqs, respectively) had a
+		 * NOT NULL value.  Those values are represented as an empty array
+		 * going forward.
+		 */
 		if (!PQgetisnull(res, rownum, i_inherited))
 			appendNamedArgument(out, fout, "inherited", "boolean",
 								PQgetvalue(res, rownum, i_inherited));
@@ -11368,6 +11375,8 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		if (!PQgetisnull(res, rownum, i_most_common_elems))
 			appendNamedArgument(out, fout, "most_common_elems", "text",
 								PQgetvalue(res, rownum, i_most_common_elems));
+		else if (fout->remoteVersion < 190000 && !PQgetisnull(res, rownum, i_most_common_elem_freqs))
+			appendNamedArgument(out, fout, "most_common_elems", "text", "{}");
 		if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
 			appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
 								PQgetvalue(res, rownum, i_most_common_elem_freqs));
@@ -11379,6 +11388,8 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 			if (!PQgetisnull(res, rownum, i_range_length_histogram))
 				appendNamedArgument(out, fout, "range_length_histogram", "text",
 									PQgetvalue(res, rownum, i_range_length_histogram));
+			else if (fout->remoteVersion < 190000 && !PQgetisnull(res, rownum, i_range_empty_frac))
+				appendNamedArgument(out, fout, "range_length_histogram", "text", "{}");
 			if (!PQgetisnull(res, rownum, i_range_empty_frac))
 				appendNamedArgument(out, fout, "range_empty_frac", "real",
 									PQgetvalue(res, rownum, i_range_empty_frac));
@@ -18718,7 +18729,27 @@ dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo)
 								 "       'most_common_freqs', ee.most_common_freqs::text, "
 								 "       'histogram_bounds', ee.histogram_bounds::text, "
 								 "       'correlation', ee.correlation::text, "
-								 "       'most_common_elems', ee.most_common_elems::text, "
+								 "       'most_common_elems', ");
+
+			/*
+			 * In versions prior to v19, most_common_elems could be NULL when
+			 * most_common_elem_freqs was NOT NULL.  In such cases the NULL
+			 * value should be replaced with the empty array "{}".
+			 *
+			 * The field range_length_histogram was not yet present in
+			 * pg_stats_ext_exprs until v19, otherwise it would have had the
+			 * same issue.
+			 */
+			if (fout->remoteVersion >= 190000)
+				appendPQExpBufferStr(pq, "ee.most_common_elems::text, ");
+			else
+				appendPQExpBufferStr(pq, "CASE "
+									 "    WHEN ee.most_common_elem_freqs IS NOT NULL "
+									 "    THEN coalesce(ee.most_common_elems::text, '{}'::text) "
+									 "    ELSE ee.most_common_elems::text "
+									 "END, ");
+
+			appendPQExpBufferStr(pq,
 								 "       'most_common_elem_freqs', ee.most_common_elem_freqs::text, "
 								 "       'elem_count_histogram', ee.elem_count_histogram::text");
 
-- 
2.56.0

