From 9798a2e6c5a22eea217269b038c89f7e522e7ba8 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Thu, 10 Sep 2026 11:08:05 -0400
Subject: [PATCH v6 2/5] pg_get_tablespace_ddl, pg_get_database_ddl:
 output-format fixes

Print tablespace options unquoted, matching pg_dumpall: they're
already "name=value" text and every option is numeric, so the generic
reloptions-printing code's quoting was both unnecessary and a needless
difference from pg_dumpall's output.  This drops the only caller of
get_reloptions() outside ruleutils.c, so make it static again.

Compare a database's tablespace against DEFAULTTABLESPACE_OID instead
of the tablespace name against the literal string "pg_default":
tablespace names are case-sensitive, so a user-created "PG_DEFAULT" is
a distinct, valid tablespace that the old comparison would misidentify.

Author: Euler Taveira <euler@eulerto.com>
Reported-by: Noah Misch <noah@leadboat.com>
Reviewed-by: Andrew Dunstan <andrew@dunslane.net>
Discussion: https://postgr.es/m/20260827015242.54.noahmisch@microsoft.com
---
 src/backend/utils/adt/ddlutils.c  | 37 +++++++++++++++++++++++++------
 src/backend/utils/adt/ruleutils.c |  4 ++--
 src/include/utils/ruleutils.h     |  1 -
 3 files changed, 32 insertions(+), 10 deletions(-)

diff --git a/src/backend/utils/adt/ddlutils.c b/src/backend/utils/adt/ddlutils.c
index 913a35fb9d9..8343cc5614f 100644
--- a/src/backend/utils/adt/ddlutils.c
+++ b/src/backend/utils/adt/ddlutils.c
@@ -40,7 +40,6 @@
 #include "utils/lsyscache.h"
 #include "utils/pg_locale.h"
 #include "utils/rel.h"
-#include "utils/ruleutils.h"
 #include "utils/syscache.h"
 #include "utils/timestamp.h"
 #include "utils/varlena.h"
@@ -597,10 +596,30 @@ pg_get_tablespace_ddl_internal(Oid tsid, bool pretty, bool no_owner)
 							Anum_pg_tablespace_spcoptions, &isNull);
 	if (!isNull)
 	{
+		Datum	   *options;
+		int			noptions;
+
 		resetStringInfo(&buf);
 		appendStringInfo(&buf, "ALTER TABLESPACE %s SET (",
 						 quote_identifier(spcname));
-		get_reloptions(&buf, datum);
+
+		/*
+		 * Elements are already "name=value", and every option is numeric,
+		 * so emit verbatim, matching pg_dumpall.
+		 */
+		deconstruct_array_builtin(DatumGetArrayTypeP(datum), TEXTOID,
+								  &options, NULL, &noptions);
+		for (int i = 0; i < noptions; i++)
+		{
+			char	   *option = TextDatumGetCString(options[i]);
+
+			if (i > 0)
+				appendStringInfoString(&buf, ", ");
+			appendStringInfoString(&buf, option);
+			pfree(option);
+		}
+		pfree(options);
+
 		appendStringInfoString(&buf, ");");
 		statements = lappend(statements, pstrdup(buf.data));
 	}
@@ -819,8 +838,13 @@ pg_get_database_ddl_internal(Oid dbid, bool pretty,
 		append_ddl_option(&buf, pretty, 4, "ICU_RULES = %s",
 						  quote_literal_cstr(TextDatumGetCString(datum)));
 
-	/* TABLESPACE */
-	if (!no_tablespace && OidIsValid(dbform->dattablespace))
+	/*
+	 * TABLESPACE.  Skip the default tablespace.  Compare by OID: tablespace
+	 * names are case-sensitive, so a user-defined "PG_DEFAULT" is a
+	 * different, valid tablespace.
+	 */
+	if (!no_tablespace && OidIsValid(dbform->dattablespace) &&
+		dbform->dattablespace != DEFAULTTABLESPACE_OID)
 	{
 		char	   *spcname = get_tablespace_name(dbform->dattablespace);
 
@@ -831,9 +855,8 @@ pg_get_database_ddl_internal(Oid dbid, bool pretty,
 							dbform->dattablespace),
 					 errdetail("It may have been concurrently dropped.")));
 
-		if (pg_strcasecmp(spcname, "pg_default") != 0)
-			append_ddl_option(&buf, pretty, 4, "TABLESPACE = %s",
-							  quote_identifier(spcname));
+		append_ddl_option(&buf, pretty, 4, "TABLESPACE = %s",
+						  quote_identifier(spcname));
 	}
 
 	appendStringInfoChar(&buf, ';');
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index f42263bf9c9..390e11a642a 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -542,7 +542,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
 static char *generate_qualified_type_name(Oid typid);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
-void		get_reloptions(StringInfo buf, Datum reloptions);
+static void get_reloptions(StringInfo buf, Datum reloptions);
 static void get_json_path_spec(Node *path_spec, deparse_context *context,
 							   bool showimplicit);
 static void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
@@ -13821,7 +13821,7 @@ string_to_text(char *str)
 /*
  * Generate a C string representing a relation options from text[] datum.
  */
-void
+static void
 get_reloptions(StringInfo buf, Datum reloptions)
 {
 	Datum	   *options;
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 25c05e2f649..059e6474f3b 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -51,7 +51,6 @@ extern char *get_window_frame_options_for_explain(int frameOptions,
 extern char *generate_collation_name(Oid collid);
 extern char *generate_opclass_name(Oid opclass);
 extern char *get_range_partbound_string(List *bound_datums);
-extern void get_reloptions(StringInfo buf, Datum reloptions);
 
 extern char *pg_get_statisticsobjdef_string(Oid statextid);
 
-- 
2.43.0

