Hi all,

While working on other things I noted that we have a lot of cases where a StringInfo instance is allocated dynamically even when it is either thrown away or destroyed at the end, which seems unnecessary, that is, instead of using:

       StringInfo info = makeStringInfo();
       ...
       appendStringInfo(info, ...);
       ...
       return info->data;

We can use

       StringInfoData info;
       initStringInfo(&info);
       ...
       appendStringInfo(&info, ...);
       ...
       return info.data;

It was corrected in an earlier commit, but that seems to have been removed so we still have a lot of these cases.

I created a semantic patch to capture most of these cases, which is present in [1], but this is a slightly modified version that might be interesting to include regardless of other changes. The patch is applied and one case that couldn't be matched is manually fixed.

[1]: https://www.postgresql.org/message-id/8895cba9-48cf-40fe-9c47-9b43ec6b2ab3%40gmail.com

Best wishes,
Mats Kindahl
From 7967dd042ada954dc57ddff734f4139c97cd6802 Mon Sep 17 00:00:00 2001
From: Mats Kindahl <[email protected]>
Date: Tue, 28 Jan 2025 14:09:41 +0100
Subject: Use stack-allocated StringInfoData

This uses a semantic patch to replace uses of StringInfo with StringInfoData
where the info is dynamically allocated but (optionally) freed at the end of
the block.  This will avoid one dynamic allocation that otherwise have to be
dealt with.

For example, this code:

    StringInfo info = makeStringInfo();
    ...
    appendStringInfo(info, ...);
    ...
    return do_stuff(..., info->data, ...);

Can be replaced with:

    StringInfoData info;
    initStringInfo(&info);
    ...
    appendStringInfo(&info, ...);
    ...
    return do_stuff(..., info.data, ...);

It does not do a replacement in these cases:

- If the variable is assigned to an expression. In this case, the pointer can
  "leak" outside the function either through a global variable or a parameter
  assignment.

- If an assignment is done to the expression. This cannot leak the data, but
  could mean a value-assignment of a structure or assigning a StringInfo to the
  variable, which is complicated to deal with, so we avoid this case.

- If the pointer is returned either using a normal return or any of the
  PG_RETURN_X versions.

The semantic patch does not handle the case where you have multiple overlapping
uses in the same block similar to the above. In this case it does not do a
replacement at all and prints an error.
---
 cocci/use_stringinfodata.cocci          | 168 ++++++++++++++++++++++++
 contrib/postgres_fdw/postgres_fdw.c     |  14 +-
 contrib/tcn/tcn.c                       |  27 ++--
 src/backend/access/transam/xlogbackup.c |  32 ++---
 src/backend/backup/basebackup.c         |  11 +-
 src/backend/commands/subscriptioncmds.c |  68 +++++-----
 src/backend/utils/adt/json.c            |  53 ++++----
 src/backend/utils/adt/jsonb.c           |   8 +-
 src/backend/utils/adt/jsonfuncs.c       |   7 +-
 src/backend/utils/adt/multirangetypes.c |  14 +-
 src/backend/utils/adt/rangetypes.c      |  17 +--
 src/backend/utils/adt/xml.c             |  33 +++--
 12 files changed, 320 insertions(+), 132 deletions(-)
 create mode 100644 cocci/use_stringinfodata.cocci

diff --git a/cocci/use_stringinfodata.cocci b/cocci/use_stringinfodata.cocci
new file mode 100644
index 00000000000..73e35f4ec89
--- /dev/null
+++ b/cocci/use_stringinfodata.cocci
@@ -0,0 +1,168 @@
+// Replace uses of StringInfo with StringInfoData where the info is
+// dynamically allocated but (optionally) freed at the end of the
+// block. This will avoid one dynamic allocation that otherwise have
+// to be dealt with.
+//
+// For example, this code:
+//
+//    StringInfo info = makeStringInfo();
+//    ...
+//    appendStringInfo(info, ...);
+//    ...
+//    return do_stuff(..., info->data, ...);
+//
+// Can be replaced with:
+//
+//    StringInfoData info;
+//    initStringInfo(&info);
+//    ...
+//    appendStringInfo(&info, ...);
+//    ...
+//    return do_stuff(..., info.data, ...);
+
+virtual report
+virtual context
+virtual patch
+
+// This rule captures the position of the makeStringInfo() and bases
+// all changes around that. It matches the case that we should *not*
+// replace, that is, those that either (1) return the pointer or (2)
+// assign the pointer to a variable or (3) assign a variable to the
+// pointer.
+//
+// The first two cases are matched because they could potentially leak
+// the pointer outside the function, for some expressions, but the
+// last one avoids assigning a StringInfo pointer of unknown source to
+// the new StringInfoData variable.
+//
+// If we replace this, the resulting change will result in a value
+// copy of a structure, which might not be optimal, so we do not do a
+// replacement.
+@id1 exists@
+typedef StringInfo;
+local idexpression StringInfo info;
+identifier f;
+position pos;
+expression E;
+identifier PG_RETURN =~ "PG_RETURN_[A-Z0-9_]+";
+@@
+  info@pos = makeStringInfo()
+  ...
+(
+  return info;
+|
+  return f(..., info, ...);
+|
+  PG_RETURN(info);
+|
+  info = E
+|
+  E = info
+)
+
+@r1 depends on !patch disable decl_init exists@
+identifier info, fld;
+position dpos, pos != id1.pos;
+@@
+(
+* StringInfo@dpos info;
+  ...
+* info@pos = makeStringInfo();
+|
+* StringInfo@dpos info@pos = makeStringInfo();
+)
+<...
+(
+* \(pfree\|destroyStringInfo\)(info);
+|
+* info->fld
+|
+* *info
+|
+* info
+)
+...>
+
+@script:python depends on report@
+info << r1.info;
+dpos << r1.dpos;
+@@
+coccilib.report.print_report(dpos[0], f"Variable '{info}' of type StringInfo can be defined using StringInfoData")
+
+@depends on patch disable decl_init exists@
+identifier info, fld;
+position pos != id1.pos;
+@@
+- StringInfo info;
++ StringInfoData info;
+  ...
+- info@pos = makeStringInfo();
++ initStringInfo(&info);
+<...
+(
+- \(destroyStringInfo\|pfree\)(info);
+|
+  info
+- ->fld
++ .fld
+|
+- *info
++ info
+|
+- info
++ &info
+)
+...>
+
+// Here we repeat the matching of the "bad case" since we cannot
+// inherit over modifications
+@id2 exists@
+typedef StringInfo;
+local idexpression StringInfo info;
+position pos;
+expression E;
+identifier f;
+identifier PG_RETURN =~ "PG_RETURN_[A-Z0-9_]+";
+@@
+  info@pos = makeStringInfo()
+  ...
+(
+  return info;
+|
+  return f(..., info, ...);
+|
+  PG_RETURN(info);
+|
+  info = E
+|
+  E = info
+)
+
+@depends on patch exists@
+identifier info, fld;
+position pos != id2.pos;
+statement S, S1;
+@@
+- StringInfo info@pos = makeStringInfo();
++ StringInfoData info;
+... when != S
+(
+<...
+(
+- \(destroyStringInfo\|pfree\)(info);
+|
+  info
+- ->fld
++ .fld
+|
+- *info
++ info
+|
+- info
++ &info
+)
+...>
+&
++ initStringInfo(&info);
+  S1
+)
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 456b267f70b..06b52c65300 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -2841,7 +2841,7 @@ postgresExplainForeignScan(ForeignScanState *node, ExplainState *es)
 	 */
 	if (list_length(fdw_private) > FdwScanPrivateRelations)
 	{
-		StringInfo	relations;
+		StringInfoData relations;
 		char	   *rawrelations;
 		char	   *ptr;
 		int			minrti,
@@ -2875,7 +2875,7 @@ postgresExplainForeignScan(ForeignScanState *node, ExplainState *es)
 		rtoffset = bms_next_member(plan->fs_base_relids, -1) - minrti;
 
 		/* Now we can translate the string */
-		relations = makeStringInfo();
+		initStringInfo(&relations);
 		ptr = rawrelations;
 		while (*ptr)
 		{
@@ -2897,24 +2897,24 @@ postgresExplainForeignScan(ForeignScanState *node, ExplainState *es)
 					char	   *namespace;
 
 					namespace = get_namespace_name_or_temp(get_rel_namespace(rte->relid));
-					appendStringInfo(relations, "%s.%s",
+					appendStringInfo(&relations, "%s.%s",
 									 quote_identifier(namespace),
 									 quote_identifier(relname));
 				}
 				else
-					appendStringInfoString(relations,
+					appendStringInfoString(&relations,
 										   quote_identifier(relname));
 				refname = (char *) list_nth(es->rtable_names, rti - 1);
 				if (refname == NULL)
 					refname = rte->eref->aliasname;
 				if (strcmp(refname, relname) != 0)
-					appendStringInfo(relations, " %s",
+					appendStringInfo(&relations, " %s",
 									 quote_identifier(refname));
 			}
 			else
-				appendStringInfoChar(relations, *ptr++);
+				appendStringInfoChar(&relations, *ptr++);
 		}
-		ExplainPropertyText("Relations", relations->data, es);
+		ExplainPropertyText("Relations", relations.data, es);
 	}
 
 	/*
diff --git a/contrib/tcn/tcn.c b/contrib/tcn/tcn.c
index 3158dee0f26..ab1d416654f 100644
--- a/contrib/tcn/tcn.c
+++ b/contrib/tcn/tcn.c
@@ -66,13 +66,14 @@ triggered_change_notification(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	char	   *channel;
 	char		operation;
-	StringInfo	payload = makeStringInfo();
+	StringInfoData payload;
 	bool		foundPK;
 
 	List	   *indexoidlist;
 	ListCell   *indexoidscan;
 
 	/* make sure it's called as a trigger */
+	initStringInfo(&payload);
 	if (!CALLED_AS_TRIGGER(fcinfo))
 		ereport(ERROR,
 				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
@@ -149,22 +150,30 @@ triggered_change_notification(PG_FUNCTION_ARGS)
 
 				foundPK = true;
 
-				strcpy_quoted(payload, RelationGetRelationName(rel), '"');
-				appendStringInfoCharMacro(payload, ',');
-				appendStringInfoCharMacro(payload, operation);
+				strcpy_quoted(&payload,
+							  RelationGetRelationName(rel),
+							  '"');
+				appendStringInfoCharMacro(&payload, ',');
+				appendStringInfoCharMacro(&payload, operation);
 
 				for (i = 0; i < indnkeyatts; i++)
 				{
 					int			colno = index->indkey.values[i];
 					Form_pg_attribute attr = TupleDescAttr(tupdesc, colno - 1);
 
-					appendStringInfoCharMacro(payload, ',');
-					strcpy_quoted(payload, NameStr(attr->attname), '"');
-					appendStringInfoCharMacro(payload, '=');
-					strcpy_quoted(payload, SPI_getvalue(trigtuple, tupdesc, colno), '\'');
+					appendStringInfoCharMacro(&payload,
+											  ',');
+					strcpy_quoted(&payload,
+								  NameStr(attr->attname),
+								  '"');
+					appendStringInfoCharMacro(&payload,
+											  '=');
+					strcpy_quoted(&payload,
+								  SPI_getvalue(trigtuple, tupdesc, colno),
+								  '\'');
 				}
 
-				Async_Notify(channel, payload->data);
+				Async_Notify(channel, payload.data);
 			}
 			ReleaseSysCache(indexTuple);
 			break;
diff --git a/src/backend/access/transam/xlogbackup.c b/src/backend/access/transam/xlogbackup.c
index cda4b38b7d6..22f579b1612 100644
--- a/src/backend/access/transam/xlogbackup.c
+++ b/src/backend/access/transam/xlogbackup.c
@@ -31,9 +31,10 @@ build_backup_content(BackupState *state, bool ishistoryfile)
 	char		startstrbuf[128];
 	char		startxlogfile[MAXFNAMELEN]; /* backup start WAL file */
 	XLogSegNo	startsegno;
-	StringInfo	result = makeStringInfo();
+	StringInfoData result;
 	char	   *data;
 
+	initStringInfo(&result);
 	Assert(state != NULL);
 
 	/* Use the log timezone here, not the session timezone */
@@ -42,7 +43,7 @@ build_backup_content(BackupState *state, bool ishistoryfile)
 
 	XLByteToSeg(state->startpoint, startsegno, wal_segment_size);
 	XLogFileName(startxlogfile, state->starttli, startsegno, wal_segment_size);
-	appendStringInfo(result, "START WAL LOCATION: %X/%08X (file %s)\n",
+	appendStringInfo(&result, "START WAL LOCATION: %X/%08X (file %s)\n",
 					 LSN_FORMAT_ARGS(state->startpoint), startxlogfile);
 
 	if (ishistoryfile)
@@ -52,18 +53,19 @@ build_backup_content(BackupState *state, bool ishistoryfile)
 
 		XLByteToSeg(state->stoppoint, stopsegno, wal_segment_size);
 		XLogFileName(stopxlogfile, state->stoptli, stopsegno, wal_segment_size);
-		appendStringInfo(result, "STOP WAL LOCATION: %X/%08X (file %s)\n",
+		appendStringInfo(&result,
+						 "STOP WAL LOCATION: %X/%08X (file %s)\n",
 						 LSN_FORMAT_ARGS(state->stoppoint), stopxlogfile);
 	}
 
-	appendStringInfo(result, "CHECKPOINT LOCATION: %X/%08X\n",
+	appendStringInfo(&result, "CHECKPOINT LOCATION: %X/%08X\n",
 					 LSN_FORMAT_ARGS(state->checkpointloc));
-	appendStringInfoString(result, "BACKUP METHOD: streamed\n");
-	appendStringInfo(result, "BACKUP FROM: %s\n",
+	appendStringInfoString(&result, "BACKUP METHOD: streamed\n");
+	appendStringInfo(&result, "BACKUP FROM: %s\n",
 					 state->started_in_recovery ? "standby" : "primary");
-	appendStringInfo(result, "START TIME: %s\n", startstrbuf);
-	appendStringInfo(result, "LABEL: %s\n", state->name);
-	appendStringInfo(result, "START TIMELINE: %u\n", state->starttli);
+	appendStringInfo(&result, "START TIME: %s\n", startstrbuf);
+	appendStringInfo(&result, "LABEL: %s\n", state->name);
+	appendStringInfo(&result, "START TIMELINE: %u\n", state->starttli);
 
 	if (ishistoryfile)
 	{
@@ -73,22 +75,22 @@ build_backup_content(BackupState *state, bool ishistoryfile)
 		pg_strftime(stopstrfbuf, sizeof(stopstrfbuf), "%Y-%m-%d %H:%M:%S %Z",
 					pg_localtime(&state->stoptime, log_timezone));
 
-		appendStringInfo(result, "STOP TIME: %s\n", stopstrfbuf);
-		appendStringInfo(result, "STOP TIMELINE: %u\n", state->stoptli);
+		appendStringInfo(&result, "STOP TIME: %s\n", stopstrfbuf);
+		appendStringInfo(&result, "STOP TIMELINE: %u\n",
+						 state->stoptli);
 	}
 
 	/* either both istartpoint and istarttli should be set, or neither */
 	Assert(XLogRecPtrIsInvalid(state->istartpoint) == (state->istarttli == 0));
 	if (!XLogRecPtrIsInvalid(state->istartpoint))
 	{
-		appendStringInfo(result, "INCREMENTAL FROM LSN: %X/%08X\n",
+		appendStringInfo(&result, "INCREMENTAL FROM LSN: %X/%08X\n",
 						 LSN_FORMAT_ARGS(state->istartpoint));
-		appendStringInfo(result, "INCREMENTAL FROM TLI: %u\n",
+		appendStringInfo(&result, "INCREMENTAL FROM TLI: %u\n",
 						 state->istarttli);
 	}
 
-	data = result->data;
-	pfree(result);
+	data = result.data;
 
 	return data;
 }
diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c
index bb7d90aa5d9..72d259344d9 100644
--- a/src/backend/backup/basebackup.c
+++ b/src/backend/backup/basebackup.c
@@ -239,7 +239,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
 	TimeLineID	endtli;
 	backup_manifest_info manifest;
 	BackupState *backup_state;
-	StringInfo	tablespace_map;
+	StringInfoData tablespace_map;
 
 	/* Initial backup state, insofar as we know it now. */
 	state.tablespaces = NIL;
@@ -263,11 +263,11 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
 
 	/* Allocate backup related variables. */
 	backup_state = (BackupState *) palloc0(sizeof(BackupState));
-	tablespace_map = makeStringInfo();
+	initStringInfo(&tablespace_map);
 
 	basebackup_progress_wait_checkpoint();
 	do_pg_backup_start(opt->label, opt->fastcheckpoint, &state.tablespaces,
-					   backup_state, tablespace_map);
+					   backup_state, &tablespace_map);
 
 	state.startptr = backup_state->startpoint;
 	state.starttli = backup_state->starttli;
@@ -342,7 +342,9 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
 				if (opt->sendtblspcmapfile)
 				{
 					sendFileWithContent(sink, TABLESPACE_MAP,
-										tablespace_map->data, -1, &manifest);
+										tablespace_map.data,
+										-1,
+										&manifest);
 					sendtblspclinks = false;
 				}
 
@@ -399,7 +401,6 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
 		endtli = backup_state->stoptli;
 
 		/* Deallocate backup-related variables. */
-		destroyStringInfo(tablespace_map);
 		pfree(backup_state);
 	}
 	PG_END_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false));
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 1f45444b499..4a095d75ba1 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -491,20 +491,19 @@ static void
 check_publications(WalReceiverConn *wrconn, List *publications)
 {
 	WalRcvExecResult *res;
-	StringInfo	cmd;
+	StringInfoData cmd;
 	TupleTableSlot *slot;
 	List	   *publicationsCopy = NIL;
 	Oid			tableRow[1] = {TEXTOID};
 
-	cmd = makeStringInfo();
-	appendStringInfoString(cmd, "SELECT t.pubname FROM\n"
+	initStringInfo(&cmd);
+	appendStringInfoString(&cmd, "SELECT t.pubname FROM\n"
 						   " pg_catalog.pg_publication t WHERE\n"
 						   " t.pubname IN (");
-	GetPublicationsStr(publications, cmd, true);
-	appendStringInfoChar(cmd, ')');
+	GetPublicationsStr(publications, &cmd, true);
+	appendStringInfoChar(&cmd, ')');
 
-	res = walrcv_exec(wrconn, cmd->data, 1, tableRow);
-	destroyStringInfo(cmd);
+	res = walrcv_exec(wrconn, cmd.data, 1, tableRow);
 
 	if (res->status != WALRCV_OK_TUPLES)
 		ereport(ERROR,
@@ -535,15 +534,16 @@ check_publications(WalReceiverConn *wrconn, List *publications)
 	if (list_length(publicationsCopy))
 	{
 		/* Prepare the list of non-existent publication(s) for error message. */
-		StringInfo	pubnames = makeStringInfo();
+		StringInfoData pubnames;
 
-		GetPublicationsStr(publicationsCopy, pubnames, false);
+		initStringInfo(&pubnames);
+		GetPublicationsStr(publicationsCopy, &pubnames, false);
 		ereport(WARNING,
 				errcode(ERRCODE_UNDEFINED_OBJECT),
 				errmsg_plural("publication %s does not exist on the publisher",
 							  "publications %s do not exist on the publisher",
 							  list_length(publicationsCopy),
-							  pubnames->data));
+							  pubnames.data));
 	}
 }
 
@@ -2597,33 +2597,38 @@ check_publications_origin_tables(WalReceiverConn *wrconn, List *publications,
 	 */
 	if (publist)
 	{
-		StringInfo	pubnames = makeStringInfo();
-		StringInfo	err_msg = makeStringInfo();
-		StringInfo	err_hint = makeStringInfo();
+		StringInfoData	pubnames;
+		StringInfoData	err_msg;
+		StringInfoData err_hint;
 
 		/* Prepare the list of publication(s) for warning message. */
-		GetPublicationsStr(publist, pubnames, false);
+		initStringInfo(&pubnames);
+		initStringInfo(&err_msg);
+		initStringInfo(&err_hint);
+		GetPublicationsStr(publist, &pubnames, false);
 
 		if (check_table_sync)
 		{
-			appendStringInfo(err_msg, _("subscription \"%s\" requested copy_data with origin = NONE but might copy data that had a different origin"),
+			appendStringInfo(&err_msg, _("subscription \"%s\" requested copy_data with origin = NONE but might copy data that had a different origin"),
 							 subname);
-			appendStringInfoString(err_hint, _("Verify that initial data copied from the publisher tables did not come from other origins."));
+			appendStringInfoString(&err_hint,
+					       _("Verify that initial data copied from the publisher tables did not come from other origins."));
 		}
 		else
 		{
-			appendStringInfo(err_msg, _("subscription \"%s\" enabled retain_dead_tuples but might not reliably detect conflicts for changes from different origins"),
+			appendStringInfo(&err_msg, _("subscription \"%s\" enabled retain_dead_tuples but might not reliably detect conflicts for changes from different origins"),
 							 subname);
-			appendStringInfoString(err_hint, _("Consider using origin = NONE or disabling retain_dead_tuples."));
+			appendStringInfoString(&err_hint,
+					       _("Consider using origin = NONE or disabling retain_dead_tuples."));
 		}
 
 		ereport(WARNING,
 				errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				errmsg_internal("%s", err_msg->data),
+				errmsg_internal("%s", err_msg.data),
 				errdetail_plural("The subscription subscribes to a publication (%s) that contains tables that are written to by other subscriptions.",
 								 "The subscription subscribes to publications (%s) that contain tables that are written to by other subscriptions.",
-								 list_length(publist), pubnames->data),
-				errhint_internal("%s", err_hint->data));
+								 list_length(publist), pubnames.data),
+				errhint_internal("%s", err_hint.data));
 	}
 
 	ExecDropSingleTupleTableSlot(slot);
@@ -2716,14 +2721,16 @@ check_publications_origin_sequences(WalReceiverConn *wrconn, List *publications,
 	{
 		StringInfo	pubnames = makeStringInfo();
 		StringInfo	err_msg = makeStringInfo();
-		StringInfo	err_hint = makeStringInfo();
+		StringInfoData err_hint;
 
 		/* Prepare the list of publication(s) for warning message. */
+		initStringInfo(&err_hint);
 		GetPublicationsStr(publist, pubnames, false);
 
 		appendStringInfo(err_msg, _("subscription \"%s\" requested copy_data with origin = NONE but might copy data that had a different origin"),
 						 subname);
-		appendStringInfoString(err_hint, _("Verify that initial data copied from the publisher sequences did not come from other origins."));
+		appendStringInfoString(&err_hint,
+				       _("Verify that initial data copied from the publisher sequences did not come from other origins."));
 
 		ereport(WARNING,
 				errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -2731,7 +2738,7 @@ check_publications_origin_sequences(WalReceiverConn *wrconn, List *publications,
 				errdetail_plural("The subscription subscribes to a publication (%s) that contains sequences that are written to by other subscriptions.",
 								 "The subscription subscribes to publications (%s) that contain sequences that are written to by other subscriptions.",
 								 list_length(publist), pubnames->data),
-				errhint_internal("%s", err_hint->data));
+				errhint_internal("%s", err_hint.data));
 	}
 
 	ExecDropSingleTupleTableSlot(slot);
@@ -2885,12 +2892,13 @@ fetch_relation_list(WalReceiverConn *wrconn, List *publications)
 	int			server_version = walrcv_server_version(wrconn);
 	bool		check_columnlist = (server_version >= 150000);
 	int			column_count = check_columnlist ? 4 : 3;
-	StringInfo	pub_names = makeStringInfo();
+	StringInfoData pub_names;
 
+	initStringInfo(&pub_names);
 	initStringInfo(&cmd);
 
 	/* Build the pub_names comma-separated string. */
-	GetPublicationsStr(publications, pub_names, true);
+	GetPublicationsStr(publications, &pub_names, true);
 
 	/* Get the list of relations from the publisher */
 	if (server_version >= 160000)
@@ -2917,7 +2925,7 @@ fetch_relation_list(WalReceiverConn *wrconn, List *publications)
 						 "                FROM pg_publication\n"
 						 "                WHERE pubname IN ( %s )) AS gpt\n"
 						 "             ON gpt.relid = c.oid\n",
-						 pub_names->data);
+						 pub_names.data);
 
 		/* From version 19, inclusion of sequences in the target is supported */
 		if (server_version >= 190000)
@@ -2926,7 +2934,7 @@ fetch_relation_list(WalReceiverConn *wrconn, List *publications)
 							 "  SELECT DISTINCT s.schemaname, s.sequencename, " CppAsString2(RELKIND_SEQUENCE) "::\"char\" AS relkind, NULL::int2vector AS attrs\n"
 							 "  FROM pg_catalog.pg_publication_sequences s\n"
 							 "  WHERE s.pubname IN ( %s )",
-							 pub_names->data);
+							 pub_names.data);
 	}
 	else
 	{
@@ -2939,11 +2947,9 @@ fetch_relation_list(WalReceiverConn *wrconn, List *publications)
 
 		appendStringInfo(&cmd, "FROM pg_catalog.pg_publication_tables t\n"
 						 " WHERE t.pubname IN ( %s )",
-						 pub_names->data);
+						 pub_names.data);
 	}
 
-	destroyStringInfo(pub_names);
-
 	res = walrcv_exec(wrconn, cmd.data, column_count, tableRow);
 	pfree(cmd.data);
 
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 88a612b041d..06dd62f0008 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -631,13 +631,13 @@ Datum
 array_to_json(PG_FUNCTION_ARGS)
 {
 	Datum		array = PG_GETARG_DATUM(0);
-	StringInfo	result;
+	StringInfoData result;
 
-	result = makeStringInfo();
+	initStringInfo(&result);
 
-	array_to_json_internal(array, result, false);
+	array_to_json_internal(array, &result, false);
 
-	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
 }
 
 /*
@@ -648,13 +648,13 @@ array_to_json_pretty(PG_FUNCTION_ARGS)
 {
 	Datum		array = PG_GETARG_DATUM(0);
 	bool		use_line_feeds = PG_GETARG_BOOL(1);
-	StringInfo	result;
+	StringInfoData result;
 
-	result = makeStringInfo();
+	initStringInfo(&result);
 
-	array_to_json_internal(array, result, use_line_feeds);
+	array_to_json_internal(array, &result, use_line_feeds);
 
-	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
 }
 
 /*
@@ -664,13 +664,13 @@ Datum
 row_to_json(PG_FUNCTION_ARGS)
 {
 	Datum		array = PG_GETARG_DATUM(0);
-	StringInfo	result;
+	StringInfoData result;
 
-	result = makeStringInfo();
+	initStringInfo(&result);
 
-	composite_to_json(array, result, false);
+	composite_to_json(array, &result, false);
 
-	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
 }
 
 /*
@@ -681,13 +681,13 @@ row_to_json_pretty(PG_FUNCTION_ARGS)
 {
 	Datum		array = PG_GETARG_DATUM(0);
 	bool		use_line_feeds = PG_GETARG_BOOL(1);
-	StringInfo	result;
+	StringInfoData result;
 
-	result = makeStringInfo();
+	initStringInfo(&result);
 
-	composite_to_json(array, result, use_line_feeds);
+	composite_to_json(array, &result, use_line_feeds);
 
-	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
 }
 
 /*
@@ -763,12 +763,13 @@ to_json(PG_FUNCTION_ARGS)
 Datum
 datum_to_json(Datum val, JsonTypeCategory tcategory, Oid outfuncoid)
 {
-	StringInfo	result = makeStringInfo();
+	StringInfoData result;
 
-	datum_to_json_internal(val, false, result, tcategory, outfuncoid,
+	initStringInfo(&result);
+	datum_to_json_internal(val, false, &result, tcategory, outfuncoid,
 						   false);
 
-	return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
+	return PointerGetDatum(cstring_to_text_with_len(result.data, result.len));
 }
 
 /*
@@ -1347,25 +1348,25 @@ json_build_array_worker(int nargs, const Datum *args, const bool *nulls, const O
 {
 	int			i;
 	const char *sep = "";
-	StringInfo	result;
+	StringInfoData result;
 
-	result = makeStringInfo();
+	initStringInfo(&result);
 
-	appendStringInfoChar(result, '[');
+	appendStringInfoChar(&result, '[');
 
 	for (i = 0; i < nargs; i++)
 	{
 		if (absent_on_null && nulls[i])
 			continue;
 
-		appendStringInfoString(result, sep);
+		appendStringInfoString(&result, sep);
 		sep = ", ";
-		add_json(args[i], nulls[i], result, types[i], false);
+		add_json(args[i], nulls[i], &result, types[i], false);
 	}
 
-	appendStringInfoChar(result, ']');
+	appendStringInfoChar(&result, ']');
 
-	return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
+	return PointerGetDatum(cstring_to_text_with_len(result.data, result.len));
 }
 
 /*
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index da94d424d61..f7e3d84f216 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -125,15 +125,15 @@ jsonb_send(PG_FUNCTION_ARGS)
 {
 	Jsonb	   *jb = PG_GETARG_JSONB_P(0);
 	StringInfoData buf;
-	StringInfo	jtext = makeStringInfo();
+	StringInfoData jtext;
 	int			version = 1;
 
-	(void) JsonbToCString(jtext, &jb->root, VARSIZE(jb));
+	initStringInfo(&jtext);
+	(void) JsonbToCString(&jtext, &jb->root, VARSIZE(jb));
 
 	pq_begintypsend(&buf);
 	pq_sendint8(&buf, version);
-	pq_sendtext(&buf, jtext->data, jtext->len);
-	destroyStringInfo(jtext);
+	pq_sendtext(&buf, jtext.data, jtext.len);
 
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 41862872e8a..936cd9dcd9a 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -4607,11 +4607,12 @@ Datum
 jsonb_pretty(PG_FUNCTION_ARGS)
 {
 	Jsonb	   *jb = PG_GETARG_JSONB_P(0);
-	StringInfo	str = makeStringInfo();
+	StringInfoData str;
 
-	JsonbToCStringIndent(str, &jb->root, VARSIZE(jb));
+	initStringInfo(&str);
+	JsonbToCStringIndent(&str, &jb->root, VARSIZE(jb));
 
-	PG_RETURN_TEXT_P(cstring_to_text_with_len(str->data, str->len));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(str.data, str.len));
 }
 
 /*
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 84733dc5019..e933b8dc02f 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -378,17 +378,18 @@ multirange_send(PG_FUNCTION_ARGS)
 {
 	MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
 	Oid			mltrngtypoid = MultirangeTypeGetOid(multirange);
-	StringInfo	buf = makeStringInfo();
+	StringInfoData buf;
 	RangeType **ranges;
 	int32		range_count;
 	MultirangeIOData *cache;
 
+	initStringInfo(&buf);
 	cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
 
 	/* construct output */
-	pq_begintypsend(buf);
+	pq_begintypsend(&buf);
 
-	pq_sendint32(buf, multirange->rangeCount);
+	pq_sendint32(&buf, multirange->rangeCount);
 
 	multirange_deserialize(cache->typcache->rngtype, multirange, &range_count, &ranges);
 	for (int i = 0; i < range_count; i++)
@@ -399,11 +400,12 @@ multirange_send(PG_FUNCTION_ARGS)
 		range = RangeTypePGetDatum(ranges[i]);
 		outputbytes = SendFunctionCall(&cache->typioproc, range);
 
-		pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
-		pq_sendbytes(buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ);
+		pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
+		pq_sendbytes(&buf, VARDATA(outputbytes),
+					 VARSIZE(outputbytes) - VARHDRSZ);
 	}
 
-	PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
 /*
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 0b2ad8b0975..c016825a00e 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -264,13 +264,14 @@ Datum
 range_send(PG_FUNCTION_ARGS)
 {
 	RangeType  *range = PG_GETARG_RANGE_P(0);
-	StringInfo	buf = makeStringInfo();
+	StringInfoData buf;
 	RangeIOData *cache;
 	char		flags;
 	RangeBound	lower;
 	RangeBound	upper;
 	bool		empty;
 
+	initStringInfo(&buf);
 	check_stack_depth();		/* recurses when subtype is a range type */
 
 	cache = get_range_io_data(fcinfo, RangeTypeGetOid(range), IOFunc_send);
@@ -280,9 +281,9 @@ range_send(PG_FUNCTION_ARGS)
 	flags = range_get_flags(range);
 
 	/* construct output */
-	pq_begintypsend(buf);
+	pq_begintypsend(&buf);
 
-	pq_sendbyte(buf, flags);
+	pq_sendbyte(&buf, flags);
 
 	if (RANGE_HAS_LBOUND(flags))
 	{
@@ -290,8 +291,8 @@ range_send(PG_FUNCTION_ARGS)
 		uint32		bound_len = VARSIZE(bound) - VARHDRSZ;
 		char	   *bound_data = VARDATA(bound);
 
-		pq_sendint32(buf, bound_len);
-		pq_sendbytes(buf, bound_data, bound_len);
+		pq_sendint32(&buf, bound_len);
+		pq_sendbytes(&buf, bound_data, bound_len);
 	}
 
 	if (RANGE_HAS_UBOUND(flags))
@@ -300,11 +301,11 @@ range_send(PG_FUNCTION_ARGS)
 		uint32		bound_len = VARSIZE(bound) - VARHDRSZ;
 		char	   *bound_data = VARDATA(bound);
 
-		pq_sendint32(buf, bound_len);
-		pq_sendbytes(buf, bound_data, bound_len);
+		pq_sendint32(&buf, bound_len);
+		pq_sendbytes(&buf, bound_data, bound_len);
 	}
 
-	PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
 /*
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 35c915573a1..b608088d1e2 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -2133,7 +2133,7 @@ xml_errorHandler(void *data, PgXmlErrorPtr error)
 						   node->type == XML_ELEMENT_NODE) ? node->name : NULL;
 	int			domain = error->domain;
 	int			level = error->level;
-	StringInfo	errorBuf;
+	StringInfoData errorBuf;
 
 	/*
 	 * Defend against someone passing us a bogus context struct.
@@ -2210,16 +2210,16 @@ xml_errorHandler(void *data, PgXmlErrorPtr error)
 	}
 
 	/* Prepare error message in errorBuf */
-	errorBuf = makeStringInfo();
+	initStringInfo(&errorBuf);
 
 	if (error->line > 0)
-		appendStringInfo(errorBuf, "line %d: ", error->line);
+		appendStringInfo(&errorBuf, "line %d: ", error->line);
 	if (name != NULL)
-		appendStringInfo(errorBuf, "element %s: ", name);
+		appendStringInfo(&errorBuf, "element %s: ", name);
 	if (error->message != NULL)
-		appendStringInfoString(errorBuf, error->message);
+		appendStringInfoString(&errorBuf, error->message);
 	else
-		appendStringInfoString(errorBuf, "(no message provided)");
+		appendStringInfoString(&errorBuf, "(no message provided)");
 
 	/*
 	 * Append context information to errorBuf.
@@ -2237,11 +2237,11 @@ xml_errorHandler(void *data, PgXmlErrorPtr error)
 		xmlGenericErrorFunc errFuncSaved = xmlGenericError;
 		void	   *errCtxSaved = xmlGenericErrorContext;
 
-		xmlSetGenericErrorFunc(errorBuf,
+		xmlSetGenericErrorFunc(&errorBuf,
 							   (xmlGenericErrorFunc) appendStringInfo);
 
 		/* Add context information to errorBuf */
-		appendStringInfoLineSeparator(errorBuf);
+		appendStringInfoLineSeparator(&errorBuf);
 
 		xmlParserPrintFileContext(input);
 
@@ -2250,7 +2250,7 @@ xml_errorHandler(void *data, PgXmlErrorPtr error)
 	}
 
 	/* Get rid of any trailing newlines in errorBuf */
-	chopStringInfoNewlines(errorBuf);
+	chopStringInfoNewlines(&errorBuf);
 
 	/*
 	 * Legacy error handling mode.  err_occurred is never set, we just add the
@@ -2263,10 +2263,9 @@ xml_errorHandler(void *data, PgXmlErrorPtr error)
 	if (xmlerrcxt->strictness == PG_XML_STRICTNESS_LEGACY)
 	{
 		appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
-		appendBinaryStringInfo(&xmlerrcxt->err_buf, errorBuf->data,
-							   errorBuf->len);
+		appendBinaryStringInfo(&xmlerrcxt->err_buf, errorBuf.data,
+							   errorBuf.len);
 
-		destroyStringInfo(errorBuf);
 		return;
 	}
 
@@ -2281,23 +2280,21 @@ xml_errorHandler(void *data, PgXmlErrorPtr error)
 	if (level >= XML_ERR_ERROR)
 	{
 		appendStringInfoLineSeparator(&xmlerrcxt->err_buf);
-		appendBinaryStringInfo(&xmlerrcxt->err_buf, errorBuf->data,
-							   errorBuf->len);
+		appendBinaryStringInfo(&xmlerrcxt->err_buf, errorBuf.data,
+							   errorBuf.len);
 
 		xmlerrcxt->err_occurred = true;
 	}
 	else if (level >= XML_ERR_WARNING)
 	{
 		ereport(WARNING,
-				(errmsg_internal("%s", errorBuf->data)));
+				(errmsg_internal("%s", errorBuf.data)));
 	}
 	else
 	{
 		ereport(NOTICE,
-				(errmsg_internal("%s", errorBuf->data)));
+				(errmsg_internal("%s", errorBuf.data)));
 	}
-
-	destroyStringInfo(errorBuf);
 }
 
 
-- 
2.43.0

Reply via email to