Replace some cstring_to_text to cstring_to_text_with_len

Started by Ranier Vilelaover 2 years ago14 messages
#1Ranier Vilela
ranier.vf@gmail.com
1 attachment(s)

Hi,

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size of the
parameter?

best regards,
Ranier Vilela

Attachments:

0003-Avoid-unecessary-calls-to-strlen-function.patchapplication/octet-stream; name=0003-Avoid-unecessary-calls-to-strlen-function.patchDownload
From 758cd2f2f6e7a9e5b649cdafedf861de0541e292 Mon Sep 17 00:00:00 2001
From: Ranier Vilela <ranier.vf@gmail.com>
Date: Wed, 30 Aug 2023 14:44:02 -0300
Subject: [PATCH 3/3] Avoid unecessary calls to strlen function.

cstring_to_text call strlen to calculate the size of string parameter.
Where we can know the size, we can avoid.

Author: Ranier Vilela (ranier.vf@gmail.com)
---
 src/backend/commands/event_trigger.c |  2 +-
 src/backend/utils/adt/arrayfuncs.c   |  2 +-
 src/backend/utils/adt/formatting.c   |  2 +-
 src/backend/utils/adt/misc.c         |  4 ++--
 src/backend/utils/adt/quote.c        |  2 +-
 src/backend/utils/adt/timestamp.c    |  5 +++--
 src/backend/utils/adt/tsquery.c      |  2 +-
 src/backend/utils/adt/varlena.c      | 18 +++++++++---------
 src/pl/plpgsql/src/pl_exec.c         |  2 +-
 9 files changed, 20 insertions(+), 19 deletions(-)

diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..a487abbd59 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -347,7 +347,7 @@ filter_list_to_array(List *filterlist)
 		result = pstrdup(value);
 		for (p = result; *p; p++)
 			*p = pg_ascii_toupper((unsigned char) *p);
-		data[i++] = PointerGetDatum(cstring_to_text(result));
+		data[i++] = PointerGetDatum(cstring_to_text_with_len(result, p - result));
 		pfree(result);
 	}
 
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 7828a6264b..b4b1fe37c3 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -1708,7 +1708,7 @@ array_dims(PG_FUNCTION_ARGS)
 		p += strlen(p);
 	}
 
-	PG_RETURN_TEXT_P(cstring_to_text(buf));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, p - buf));
 }
 
 /*
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index e27ea8ef97..cc6db71d81 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -6059,7 +6059,7 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout,
 do { \
 	int len = VARSIZE_ANY_EXHDR(fmt); \
 	if (len <= 0 || len >= (INT_MAX-VARHDRSZ)/NUM_MAX_ITEM_SIZ)		\
-		PG_RETURN_TEXT_P(cstring_to_text("")); \
+		PG_RETURN_TEXT_P(cstring_to_text_with_len("", 0)); \
 	result	= (text *) palloc0((len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ);	\
 	format	= NUM_cache(len, &Num, fmt, &shouldFree);		\
 } while (0)
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5d78d6dc06..ee45639720 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -319,7 +319,7 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
 	 */
 	if (tablespaceOid == DEFAULTTABLESPACE_OID ||
 		tablespaceOid == GLOBALTABLESPACE_OID)
-		PG_RETURN_TEXT_P(cstring_to_text(""));
+		PG_RETURN_TEXT_P(cstring_to_text_with_len("", 0));
 
 	/*
 	 * Find the location of the tablespace by reading the symbolic link that
@@ -360,7 +360,7 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
 						sourcepath)));
 	targetpath[rllen] = '\0';
 
-	PG_RETURN_TEXT_P(cstring_to_text(targetpath));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(targetpath, rllen));
 }
 
 /*
diff --git a/src/backend/utils/adt/quote.c b/src/backend/utils/adt/quote.c
index f2f633befa..441f94581a 100644
--- a/src/backend/utils/adt/quote.c
+++ b/src/backend/utils/adt/quote.c
@@ -125,7 +125,7 @@ Datum
 quote_nullable(PG_FUNCTION_ARGS)
 {
 	if (PG_ARGISNULL(0))
-		PG_RETURN_TEXT_P(cstring_to_text("NULL"));
+		PG_RETURN_TEXT_P(cstring_to_text_with_len("NULL", 4));
 	else
 		PG_RETURN_DATUM(DirectFunctionCall1(quote_literal,
 											PG_GETARG_DATUM(0)));
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 0e50aaec5a..4a91e4cf75 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -1632,14 +1632,15 @@ timeofday(PG_FUNCTION_ARGS)
 	char		templ[128];
 	char		buf[128];
 	pg_time_t	tt;
+	int			len;
 
 	gettimeofday(&tp, NULL);
 	tt = (pg_time_t) tp.tv_sec;
 	pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z",
 				pg_localtime(&tt, session_timezone));
-	snprintf(buf, sizeof(buf), templ, tp.tv_usec);
+	len = snprintf(buf, sizeof(buf), templ, tp.tv_usec);
 
-	PG_RETURN_TEXT_P(cstring_to_text(buf));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, len));
 }
 
 /*
diff --git a/src/backend/utils/adt/tsquery.c b/src/backend/utils/adt/tsquery.c
index 67ad876a27..3855677f0d 100644
--- a/src/backend/utils/adt/tsquery.c
+++ b/src/backend/utils/adt/tsquery.c
@@ -1382,7 +1382,7 @@ tsquerytree(PG_FUNCTION_ARGS)
 
 	if (!q)
 	{
-		res = cstring_to_text("T");
+		res = cstring_to_text_with_len("T", 1);
 	}
 	else
 	{
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 72e1e24fe0..495dff4e65 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -924,7 +924,7 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 			 * string.
 			 */
 			if (E < 1)
-				return cstring_to_text("");
+				return cstring_to_text_with_len("", 0);
 
 			L1 = E - S1;
 		}
@@ -987,7 +987,7 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 			 * string.
 			 */
 			if (E < 1)
-				return cstring_to_text("");
+				return cstring_to_text_with_len("", 0);
 
 			/*
 			 * if E is past the end of the string, the tuple toaster will
@@ -1019,7 +1019,7 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 		{
 			if (slice != (text *) DatumGetPointer(str))
 				pfree(slice);
-			return cstring_to_text("");
+			return cstring_to_text_with_len("", 0);
 		}
 
 		/* Now we can get the actual length of the slice in MB characters */
@@ -1034,7 +1034,7 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 		{
 			if (slice != (text *) DatumGetPointer(str))
 				pfree(slice);
-			return cstring_to_text("");
+			return cstring_to_text_with_len("", 0);
 		}
 
 		/*
@@ -4386,7 +4386,7 @@ split_part(PG_FUNCTION_ARGS)
 
 	/* return empty string for empty input string */
 	if (inputstring_len < 1)
-		PG_RETURN_TEXT_P(cstring_to_text(""));
+		PG_RETURN_TEXT_P(cstring_to_text_with_len("", 0));
 
 	/* handle empty field separator */
 	if (fldsep_len < 1)
@@ -4395,7 +4395,7 @@ split_part(PG_FUNCTION_ARGS)
 		if (fldnum == 1 || fldnum == -1)
 			PG_RETURN_TEXT_P(inputstring);
 		else
-			PG_RETURN_TEXT_P(cstring_to_text(""));
+			PG_RETURN_TEXT_P(cstring_to_text_with_len("", 0));
 	}
 
 	/* find the first field separator */
@@ -4411,7 +4411,7 @@ split_part(PG_FUNCTION_ARGS)
 		if (fldnum == 1 || fldnum == -1)
 			PG_RETURN_TEXT_P(inputstring);
 		else
-			PG_RETURN_TEXT_P(cstring_to_text(""));
+			PG_RETURN_TEXT_P(cstring_to_text_with_len("", 0));
 	}
 
 	/*
@@ -4443,7 +4443,7 @@ split_part(PG_FUNCTION_ARGS)
 		if (fldnum <= 0)
 		{
 			text_position_cleanup(&state);
-			PG_RETURN_TEXT_P(cstring_to_text(""));
+			PG_RETURN_TEXT_P(cstring_to_text_with_len("", 0));
 		}
 
 		/* reset to pointing at first match, but now with positive fldnum */
@@ -4479,7 +4479,7 @@ split_part(PG_FUNCTION_ARGS)
 												   inputstring_len - last_len);
 		}
 		else
-			result_text = cstring_to_text("");
+			result_text = cstring_to_text_with_len("", 0);
 	}
 	else
 	{
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 4b76f7699a..b84ce322e2 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -5058,7 +5058,7 @@ exec_assign_c_string(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
 	if (str != NULL)
 		value = cstring_to_text(str);
 	else
-		value = cstring_to_text("");
+		value = cstring_to_text_with_len("", 0);
 	MemoryContextSwitchTo(oldcontext);
 
 	exec_assign_value(estate, target, PointerGetDatum(value), false,
-- 
2.32.0.windows.1

#2Michael Paquier
michael@paquier.xyz
In reply to: Ranier Vilela (#1)
Re: Replace some cstring_to_text to cstring_to_text_with_len

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size of the
parameter?

Are there workloads where this matters?
--
Michael

#3Ranier Vilela
ranier.vf@gmail.com
In reply to: Michael Paquier (#2)
Re: Replace some cstring_to_text to cstring_to_text_with_len

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier <michael@paquier.xyz>
escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size of the
parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc
<https://github.com/postgres/postgres/commit/8b26769bc441fffa8aad31dddc484c2f4043d2c9&gt;
.

best regards,
Ranier Vilela

#4John Naylor
john.naylor@enterprisedb.com
In reply to: Ranier Vilela (#3)
Re: Replace some cstring_to_text to cstring_to_text_with_len

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com> wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier <michael@paquier.xyz>

escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size of the
parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

@@ -360,7 +360,7 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
sourcepath)));
targetpath[rllen] = '\0';

- PG_RETURN_TEXT_P(cstring_to_text(targetpath));
+ PG_RETURN_TEXT_P(cstring_to_text_with_len(targetpath, rllen));

This could be a worthwhile cosmetic improvement if the nul-terminator (and
space reserved for it, and comment explaining that) is taken out as well,
but the patch didn't bother to do that.

--
John Naylor
EDB: http://www.enterprisedb.com

#5Andrew Dunstan
andrew@dunslane.net
In reply to: John Naylor (#4)
Re: Replace some cstring_to_text to cstring_to_text_with_len

On 2023-08-31 Th 07:41, John Naylor wrote:

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com> wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier

<michael@paquier.xyz> escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size

of the

parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

I agree this is a bit ugly. I wonder if we'd be better off creating a
function that returned an empty text value, so we'd just avoid
converting the empty cstring altogether and say:

  return empty_text();

cheers

andrew

--
Andrew Dunstan
EDB:https://www.enterprisedb.com

In reply to: Andrew Dunstan (#5)
Re: Replace some cstring_to_text to cstring_to_text_with_len

Andrew Dunstan <andrew@dunslane.net> writes:

On 2023-08-31 Th 07:41, John Naylor wrote:

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com> wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier

<michael@paquier.xyz> escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size

of the

parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

I agree this is a bit ugly. I wonder if we'd be better off creating a
function that returned an empty text value, so we'd just avoid
converting the empty cstring altogether and say:

  return empty_text();

Or we could generalise it for any string literal (of which there are
slightly more¹ non-empty than empty in calls to
cstring_to_text(_with_len)):

#define literal_to_text(str) cstring_to_text_with_len("" str "", sizeof(str)-1)

[1]: ~/src/postgresql $ rg 'cstring_to_text(_with_len)?\("[^"]+"' | wc -l 17 ~/src/postgresql $ rg 'cstring_to_text(_with_len)?\(""' | wc -l 15

~/src/postgresql $ rg 'cstring_to_text(_with_len)?\("[^"]+"' | wc -l
17
~/src/postgresql $ rg 'cstring_to_text(_with_len)?\(""' | wc -l
15

- ilmari

#7Ranier Vilela
ranier.vf@gmail.com
In reply to: John Naylor (#4)
Re: Replace some cstring_to_text to cstring_to_text_with_len

Em qui., 31 de ago. de 2023 às 08:41, John Naylor <
john.naylor@enterprisedb.com> escreveu:

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com> wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier <

michael@paquier.xyz> escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size of the
parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

Per suggestion by Andrew Dustan, I provided a new function.

@@ -360,7 +360,7 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
sourcepath)));
targetpath[rllen] = '\0';

- PG_RETURN_TEXT_P(cstring_to_text(targetpath));
+ PG_RETURN_TEXT_P(cstring_to_text_with_len(targetpath, rllen));

This could be a worthwhile cosmetic improvement if the nul-terminator (and
space reserved for it, and comment explaining that) is taken out as well,
but the patch didn't bother to do that.

Thanks for the tip.

Please see a new version of the patch in the Andrew Dunstan, reply.

best regards,
Ranier Vilela

#8Ranier Vilela
ranier.vf@gmail.com
In reply to: Andrew Dunstan (#5)
2 attachment(s)
Re: Replace some cstring_to_text to cstring_to_text_with_len

Em qui., 31 de ago. de 2023 às 09:51, Andrew Dunstan <andrew@dunslane.net>
escreveu:

On 2023-08-31 Th 07:41, John Naylor wrote:

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com> wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier <

michael@paquier.xyz> escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size of the
parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

I agree this is a bit ugly. I wonder if we'd be better off creating a
function that returned an empty text value, so we'd just avoid converting
the empty cstring altogether and say:

return empty_text();

Hi,
Thanks for the suggestion, I agreed.

New patch is attached.

best regards,
Ranier Vilela

Show quoted text

Attachments:

0001-Replace-cstring_to_text-with-empty_text.patchapplication/octet-stream; name=0001-Replace-cstring_to_text-with-empty_text.patchDownload
From 70d827c851facc1a9cf79642444e7b770cb5296e Mon Sep 17 00:00:00 2001
From: Ranier Vilela <ranier.vf@gmail.com>
Date: Thu, 31 Aug 2023 10:48:00 -0300
Subject: [PATCH 1/2] Replace cstring_to_text with empty_text

Use the new function empty_text where cstring_to_text and
cstring_to_text_with_len are used with "".

Author: Ranier Vilela (ranier.vf@gmail.com)
---
 contrib/btree_gin/btree_gin.c         |  2 +-
 contrib/fuzzystrmatch/fuzzystrmatch.c |  2 +-
 src/backend/utils/adt/formatting.c    |  2 +-
 src/backend/utils/adt/misc.c          |  2 +-
 src/backend/utils/adt/varlena.c       | 37 +++++++++++++++++++--------
 src/include/utils/builtins.h          |  1 +
 src/pl/plpgsql/src/pl_exec.c          |  2 +-
 7 files changed, 33 insertions(+), 15 deletions(-)

diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c
index c50d68ce18..f8b596a8c3 100644
--- a/contrib/btree_gin/btree_gin.c
+++ b/contrib/btree_gin/btree_gin.c
@@ -347,7 +347,7 @@ GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
 static Datum
 leftmostvalue_text(void)
 {
-	return PointerGetDatum(cstring_to_text_with_len("", 0));
+	return PointerGetDatum(empty_text());
 }
 
 GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
diff --git a/contrib/fuzzystrmatch/fuzzystrmatch.c b/contrib/fuzzystrmatch/fuzzystrmatch.c
index 5686497983..1d385b352e 100644
--- a/contrib/fuzzystrmatch/fuzzystrmatch.c
+++ b/contrib/fuzzystrmatch/fuzzystrmatch.c
@@ -264,7 +264,7 @@ metaphone(PG_FUNCTION_ARGS)
 
 	/* return an empty string if we receive one */
 	if (!(str_i_len > 0))
-		PG_RETURN_TEXT_P(cstring_to_text(""));
+		PG_RETURN_TEXT_P(empty_text());
 
 	if (str_i_len > MAX_METAPHONE_STRLEN)
 		ereport(ERROR,
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index e27ea8ef97..4d1d1fba3b 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -6059,7 +6059,7 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout,
 do { \
 	int len = VARSIZE_ANY_EXHDR(fmt); \
 	if (len <= 0 || len >= (INT_MAX-VARHDRSZ)/NUM_MAX_ITEM_SIZ)		\
-		PG_RETURN_TEXT_P(cstring_to_text("")); \
+		PG_RETURN_TEXT_P(empty_text()); \
 	result	= (text *) palloc0((len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ);	\
 	format	= NUM_cache(len, &Num, fmt, &shouldFree);		\
 } while (0)
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5d78d6dc06..7cc2bc4e39 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -319,7 +319,7 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
 	 */
 	if (tablespaceOid == DEFAULTTABLESPACE_OID ||
 		tablespaceOid == GLOBALTABLESPACE_OID)
-		PG_RETURN_TEXT_P(cstring_to_text(""));
+		PG_RETURN_TEXT_P(empty_text());
 
 	/*
 	 * Find the location of the tablespace by reading the symbolic link that
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 72e1e24fe0..644b777d77 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -171,6 +171,23 @@ static void text_format_append_string(StringInfo buf, const char *str,
  *	 CONVERSION ROUTINES EXPORTED FOR USE BY C CODE							 *
  *****************************************************************************/
 
+/*
+ * empty_text
+ *
+ * Create a empty text value
+ *
+ * The new text value is freshly palloc'd with a full-size VARHDR.
+ */
+text *
+empty_text(void)
+{
+	text	   *result = (text *) palloc(VARHDRSZ);
+
+	SET_VARSIZE(result, VARHDRSZ);
+
+	return result;
+}
+
 /*
  * cstring_to_text
  *
@@ -924,7 +941,7 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 			 * string.
 			 */
 			if (E < 1)
-				return cstring_to_text("");
+				return empty_text();
 
 			L1 = E - S1;
 		}
@@ -987,7 +1004,7 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 			 * string.
 			 */
 			if (E < 1)
-				return cstring_to_text("");
+				return empty_text();
 
 			/*
 			 * if E is past the end of the string, the tuple toaster will
@@ -1019,7 +1036,7 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 		{
 			if (slice != (text *) DatumGetPointer(str))
 				pfree(slice);
-			return cstring_to_text("");
+			return empty_text();
 		}
 
 		/* Now we can get the actual length of the slice in MB characters */
@@ -1034,7 +1051,7 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 		{
 			if (slice != (text *) DatumGetPointer(str))
 				pfree(slice);
-			return cstring_to_text("");
+			return empty_text();
 		}
 
 		/*
@@ -4386,7 +4403,7 @@ split_part(PG_FUNCTION_ARGS)
 
 	/* return empty string for empty input string */
 	if (inputstring_len < 1)
-		PG_RETURN_TEXT_P(cstring_to_text(""));
+		PG_RETURN_TEXT_P(empty_text());
 
 	/* handle empty field separator */
 	if (fldsep_len < 1)
@@ -4395,7 +4412,7 @@ split_part(PG_FUNCTION_ARGS)
 		if (fldnum == 1 || fldnum == -1)
 			PG_RETURN_TEXT_P(inputstring);
 		else
-			PG_RETURN_TEXT_P(cstring_to_text(""));
+			PG_RETURN_TEXT_P(empty_text());
 	}
 
 	/* find the first field separator */
@@ -4411,7 +4428,7 @@ split_part(PG_FUNCTION_ARGS)
 		if (fldnum == 1 || fldnum == -1)
 			PG_RETURN_TEXT_P(inputstring);
 		else
-			PG_RETURN_TEXT_P(cstring_to_text(""));
+			PG_RETURN_TEXT_P(empty_text());
 	}
 
 	/*
@@ -4443,7 +4460,7 @@ split_part(PG_FUNCTION_ARGS)
 		if (fldnum <= 0)
 		{
 			text_position_cleanup(&state);
-			PG_RETURN_TEXT_P(cstring_to_text(""));
+			PG_RETURN_TEXT_P(empty_text());
 		}
 
 		/* reset to pointing at first match, but now with positive fldnum */
@@ -4479,7 +4496,7 @@ split_part(PG_FUNCTION_ARGS)
 												   inputstring_len - last_len);
 		}
 		else
-			result_text = cstring_to_text("");
+			result_text = empty_text();
 	}
 	else
 	{
@@ -4827,7 +4844,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
 
 	/* if there are no elements, return an empty string */
 	if (nitems == 0)
-		return cstring_to_text_with_len("", 0);
+		return empty_text();
 
 	element_type = ARR_ELEMTYPE(v);
 	initStringInfo(&buf);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 2f8b46d6da..bb1c4e2055 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -86,6 +86,7 @@ extern void generate_operator_clause(fmStringInfo buf,
 extern int	bpchartruelen(char *s, int len);
 
 /* popular functions from varlena.c */
+extern text *empty_text(void);
 extern text *cstring_to_text(const char *s);
 extern text *cstring_to_text_with_len(const char *s, int len);
 extern char *text_to_cstring(const text *t);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 4b76f7699a..0d744e1f92 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -5058,7 +5058,7 @@ exec_assign_c_string(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
 	if (str != NULL)
 		value = cstring_to_text(str);
 	else
-		value = cstring_to_text("");
+		value = empty_text();
 	MemoryContextSwitchTo(oldcontext);
 
 	exec_assign_value(estate, target, PointerGetDatum(value), false,
-- 
2.32.0.windows.1

0002-Avoid-unecessary-calls-to-strlen-function.patchapplication/octet-stream; name=0002-Avoid-unecessary-calls-to-strlen-function.patchDownload
From 9aaec14a0601625bc2c4a5e1225bb58fa7237e4a Mon Sep 17 00:00:00 2001
From: Ranier Vilela <ranier.vf@gmail.com>
Date: Thu, 31 Aug 2023 10:59:54 -0300
Subject: [PATCH 2/2] Avoid unecessary calls to strlen function.

cstring_to_text call strlen to calculate the size of string parameter.
Where we can know the size, we can avoid.

Author: Ranier Vilela (ranier.vf@gmail.com)
---
 src/backend/commands/event_trigger.c | 2 +-
 src/backend/utils/adt/arrayfuncs.c   | 2 +-
 src/backend/utils/adt/misc.c         | 3 +--
 src/backend/utils/adt/quote.c        | 2 +-
 src/backend/utils/adt/timestamp.c    | 5 +++--
 src/backend/utils/adt/tsquery.c      | 2 +-
 6 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..a487abbd59 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -347,7 +347,7 @@ filter_list_to_array(List *filterlist)
 		result = pstrdup(value);
 		for (p = result; *p; p++)
 			*p = pg_ascii_toupper((unsigned char) *p);
-		data[i++] = PointerGetDatum(cstring_to_text(result));
+		data[i++] = PointerGetDatum(cstring_to_text_with_len(result, p - result));
 		pfree(result);
 	}
 
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 7828a6264b..b4b1fe37c3 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -1708,7 +1708,7 @@ array_dims(PG_FUNCTION_ARGS)
 		p += strlen(p);
 	}
 
-	PG_RETURN_TEXT_P(cstring_to_text(buf));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, p - buf));
 }
 
 /*
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 7cc2bc4e39..f0349e1e91 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -358,9 +358,8 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("symbolic link \"%s\" target is too long",
 						sourcepath)));
-	targetpath[rllen] = '\0';
 
-	PG_RETURN_TEXT_P(cstring_to_text(targetpath));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(targetpath, rllen));
 }
 
 /*
diff --git a/src/backend/utils/adt/quote.c b/src/backend/utils/adt/quote.c
index f2f633befa..441f94581a 100644
--- a/src/backend/utils/adt/quote.c
+++ b/src/backend/utils/adt/quote.c
@@ -125,7 +125,7 @@ Datum
 quote_nullable(PG_FUNCTION_ARGS)
 {
 	if (PG_ARGISNULL(0))
-		PG_RETURN_TEXT_P(cstring_to_text("NULL"));
+		PG_RETURN_TEXT_P(cstring_to_text_with_len("NULL", 4));
 	else
 		PG_RETURN_DATUM(DirectFunctionCall1(quote_literal,
 											PG_GETARG_DATUM(0)));
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 0e50aaec5a..4a91e4cf75 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -1632,14 +1632,15 @@ timeofday(PG_FUNCTION_ARGS)
 	char		templ[128];
 	char		buf[128];
 	pg_time_t	tt;
+	int			len;
 
 	gettimeofday(&tp, NULL);
 	tt = (pg_time_t) tp.tv_sec;
 	pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z",
 				pg_localtime(&tt, session_timezone));
-	snprintf(buf, sizeof(buf), templ, tp.tv_usec);
+	len = snprintf(buf, sizeof(buf), templ, tp.tv_usec);
 
-	PG_RETURN_TEXT_P(cstring_to_text(buf));
+	PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, len));
 }
 
 /*
diff --git a/src/backend/utils/adt/tsquery.c b/src/backend/utils/adt/tsquery.c
index 67ad876a27..3855677f0d 100644
--- a/src/backend/utils/adt/tsquery.c
+++ b/src/backend/utils/adt/tsquery.c
@@ -1382,7 +1382,7 @@ tsquerytree(PG_FUNCTION_ARGS)
 
 	if (!q)
 	{
-		res = cstring_to_text("T");
+		res = cstring_to_text_with_len("T", 1);
 	}
 	else
 	{
-- 
2.32.0.windows.1

#9Ranier Vilela
ranier.vf@gmail.com
In reply to: Dagfinn Ilmari Mannsåker (#6)
Re: Replace some cstring_to_text to cstring_to_text_with_len

Em qui., 31 de ago. de 2023 às 10:12, Dagfinn Ilmari Mannsåker <
ilmari@ilmari.org> escreveu:

Andrew Dunstan <andrew@dunslane.net> writes:

On 2023-08-31 Th 07:41, John Naylor wrote:

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com>

wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier

<michael@paquier.xyz> escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size

of the

parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

I agree this is a bit ugly. I wonder if we'd be better off creating a
function that returned an empty text value, so we'd just avoid
converting the empty cstring altogether and say:

return empty_text();

Or we could generalise it for any string literal (of which there are
slightly more¹ non-empty than empty in calls to
cstring_to_text(_with_len)):

#define literal_to_text(str) cstring_to_text_with_len("" str "",
sizeof(str)-1)

I do not agree, I think this will get worse.

best regards,
Ranier Vilela

In reply to: Ranier Vilela (#9)
Re: Replace some cstring_to_text to cstring_to_text_with_len

Ranier Vilela <ranier.vf@gmail.com> writes:

Em qui., 31 de ago. de 2023 às 10:12, Dagfinn Ilmari Mannsåker <
ilmari@ilmari.org> escreveu:

Andrew Dunstan <andrew@dunslane.net> writes:

On 2023-08-31 Th 07:41, John Naylor wrote:

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com>

wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier

<michael@paquier.xyz> escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size

of the

parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

I agree this is a bit ugly. I wonder if we'd be better off creating a
function that returned an empty text value, so we'd just avoid
converting the empty cstring altogether and say:

return empty_text();

Or we could generalise it for any string literal (of which there are
slightly more¹ non-empty than empty in calls to
cstring_to_text(_with_len)):

#define literal_to_text(str) cstring_to_text_with_len("" str "",
sizeof(str)-1)

I do not agree, I think this will get worse.

How exactly will it get worse? It's exactly equivalent to
cstring_to_text_with_len("", 0), since sizeof() is a compile-time
construct, and the string concatenation makes it fail if the argument is
not a literal string.

Whether we want an even-more-optimised version for an empty text value
is another matter, but I doubt it'd be worth it. Another option would
be to make cstring_to_text(_with_len) static inline functions, which
lets the compiler eliminate the memcpy() call when len == 0.

In fact, after playing around a bit (https://godbolt.org/z/x51aYGadh),
it seems like GCC, Clang and MSVC all eliminate the strlen() and
memcpy() calls for cstring_to_text("") under -O2 if it's static inline.

- ilmari

#11Peter Eisentraut
peter@eisentraut.org
In reply to: Ranier Vilela (#8)
Re: Replace some cstring_to_text to cstring_to_text_with_len

On 31.08.23 16:10, Ranier Vilela wrote:

Em qui., 31 de ago. de 2023 às 09:51, Andrew Dunstan
<andrew@dunslane.net <mailto:andrew@dunslane.net>> escreveu:

On 2023-08-31 Th 07:41, John Naylor wrote:

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com
<mailto:ranier.vf@gmail.com>> wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier

<michael@paquier.xyz <mailto:michael@paquier.xyz>> escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the

size of the

parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

I agree this is a bit ugly. I wonder if we'd be better off creating
a function that returned an empty text value, so we'd just avoid
converting the empty cstring altogether and say:

  return empty_text();

Hi,
Thanks for the suggestion,  I agreed.

New patch is attached.

I think these patches make the code uniformly uglier and harder to
understand.

If a performance benefit could be demonstrated, then making
cstring_to_text() an inline function could be sensible. But I wouldn't
go beyond that.

#12Ranier Vilela
ranier.vf@gmail.com
In reply to: Dagfinn Ilmari Mannsåker (#10)
Re: Replace some cstring_to_text to cstring_to_text_with_len

Em qui., 31 de ago. de 2023 às 12:12, Dagfinn Ilmari Mannsåker <
ilmari@ilmari.org> escreveu:

Ranier Vilela <ranier.vf@gmail.com> writes:

Em qui., 31 de ago. de 2023 às 10:12, Dagfinn Ilmari Mannsåker <
ilmari@ilmari.org> escreveu:

Andrew Dunstan <andrew@dunslane.net> writes:

On 2023-08-31 Th 07:41, John Naylor wrote:

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com>

wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier

<michael@paquier.xyz> escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size

of the

parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

I agree this is a bit ugly. I wonder if we'd be better off creating a
function that returned an empty text value, so we'd just avoid
converting the empty cstring altogether and say:

return empty_text();

Or we could generalise it for any string literal (of which there are
slightly more¹ non-empty than empty in calls to
cstring_to_text(_with_len)):

#define literal_to_text(str) cstring_to_text_with_len("" str "",
sizeof(str)-1)

I do not agree, I think this will get worse.

How exactly will it get worse? It's exactly equivalent to
cstring_to_text_with_len("", 0), since sizeof() is a compile-time
construct, and the string concatenation makes it fail if the argument is
not a literal string.

I think that concatenation makes the strings twice bigger, doesn't it?

Whether we want an even-more-optimised version for an empty text value
is another matter, but I doubt it'd be worth it. Another option would
be to make cstring_to_text(_with_len) static inline functions, which
lets the compiler eliminate the memcpy() call when len == 0.

In fact, after playing around a bit (https://godbolt.org/z/x51aYGadh),
it seems like GCC, Clang and MSVC all eliminate the strlen() and
memcpy() calls for cstring_to_text("") under -O2 if it's static inline.

In that case, it seems to me that would be good too.
Compilers removing memcpy would have the same as empty_text.
Without the burden of a new function and all its future maintenance.

best regards,
Ranier Vilela

In reply to: Ranier Vilela (#12)
Re: Replace some cstring_to_text to cstring_to_text_with_len

Ranier Vilela <ranier.vf@gmail.com> writes:

Em qui., 31 de ago. de 2023 às 12:12, Dagfinn Ilmari Mannsåker <
ilmari@ilmari.org> escreveu:

Ranier Vilela <ranier.vf@gmail.com> writes:

Em qui., 31 de ago. de 2023 às 10:12, Dagfinn Ilmari Mannsåker <
ilmari@ilmari.org> escreveu:

Andrew Dunstan <andrew@dunslane.net> writes:

On 2023-08-31 Th 07:41, John Naylor wrote:

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com>

wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier

<michael@paquier.xyz> escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the size

of the

parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

I agree this is a bit ugly. I wonder if we'd be better off creating a
function that returned an empty text value, so we'd just avoid
converting the empty cstring altogether and say:

return empty_text();

Or we could generalise it for any string literal (of which there are
slightly more¹ non-empty than empty in calls to
cstring_to_text(_with_len)):

#define literal_to_text(str) cstring_to_text_with_len("" str "",
sizeof(str)-1)

I do not agree, I think this will get worse.

How exactly will it get worse? It's exactly equivalent to
cstring_to_text_with_len("", 0), since sizeof() is a compile-time
construct, and the string concatenation makes it fail if the argument is
not a literal string.

I think that concatenation makes the strings twice bigger, doesn't it?

No, it's just taking advantage of the fact that C string literals can be
split into multiple pieces separated by whitespace (like in SQL, but
without requiring a newline between them). E.g. "foo" "bar" is exactly
equivalent to "foobar" after parsing. The reason to use it in the macro
is to make it a syntax error if the argument is not a literal string but
instead a string variable, becuause in that case the sizeof() would
return the size of the pointer, not the string.

- ilmari

In reply to: Peter Eisentraut (#11)
1 attachment(s)
Re: Replace some cstring_to_text to cstring_to_text_with_len

Peter Eisentraut <peter@eisentraut.org> writes:

On 31.08.23 16:10, Ranier Vilela wrote:

Em qui., 31 de ago. de 2023 às 09:51, Andrew Dunstan
<andrew@dunslane.net <mailto:andrew@dunslane.net>> escreveu:

On 2023-08-31 Th 07:41, John Naylor wrote:

On Thu, Aug 31, 2023 at 6:07 PM Ranier Vilela <ranier.vf@gmail.com
<mailto:ranier.vf@gmail.com>> wrote:

Em qui., 31 de ago. de 2023 às 00:22, Michael Paquier

<michael@paquier.xyz <mailto:michael@paquier.xyz>> escreveu:

On Wed, Aug 30, 2023 at 03:00:13PM -0300, Ranier Vilela wrote:

cstring_to_text has a small overhead, because call strlen for
pointer to char parameter.

Is it worth the effort to avoid this, where do we know the

size of the

parameter?

Are there workloads where this matters?

None, but note this change has the same spirit of 8b26769bc.

- return cstring_to_text("");
+ return cstring_to_text_with_len("", 0);

This looks worse, so we'd better be getting something in return.

I agree this is a bit ugly. I wonder if we'd be better off creating
a function that returned an empty text value, so we'd just avoid
converting the empty cstring altogether and say:
  return empty_text();
Hi,
Thanks for the suggestion,  I agreed.
New patch is attached.

I think these patches make the code uniformly uglier and harder to
understand.

If a performance benefit could be demonstrated, then making
cstring_to_text() an inline function could be sensible. But I wouldn't
go beyond that.

I haven't benchmarked it yet, but here's a patch that inlines them and
changes callers of cstring_to_text_with_len() with a aliteral string and
constant length to cstring_to_text().

On an x86-64 Linux build (meson with -Dbuildtype=debugoptimized
-Dcassert=true), the inlining increases the size of the text section of
the postgres binary from 9719722 bytes to 9750557, i.e. an increase of
30KiB or 0.3%, while the change to cstring_to_text() makes zero
difference (as expected from my investigation).

- ilmari

Attachments:

0001-Inline-cstring_to_text-_with_len-functions.patchtext/x-diffDownload
From e332e5229dafd94e44ad8608c5fa2d3c58d80e0e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= <ilmari@ilmari.org>
Date: Thu, 31 Aug 2023 19:11:55 +0100
Subject: [PATCH] Inline cstring_to_text(_with_len) functions

This lets the compiler optimise out the strlen() and memcpy() calls
when they are called with a literal string or constant length.

Thus, convert cstring_to_text_with_length("literal", 7) calls to plain
cstring_to_text("literal") to avoid having to count string lenghts
manually.
---
 contrib/btree_gin/btree_gin.c     |  2 +-
 contrib/hstore/hstore_io.c        |  4 ++--
 src/backend/utils/adt/json.c      |  4 ++--
 src/backend/utils/adt/jsonfuncs.c |  4 ++--
 src/backend/utils/adt/varlena.c   | 32 +----------------------------
 src/include/utils/builtins.h      | 34 +++++++++++++++++++++++++++++--
 6 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c
index c50d68ce18..59db475791 100644
--- a/contrib/btree_gin/btree_gin.c
+++ b/contrib/btree_gin/btree_gin.c
@@ -347,7 +347,7 @@ GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
 static Datum
 leftmostvalue_text(void)
 {
-	return PointerGetDatum(cstring_to_text_with_len("", 0));
+	return PointerGetDatum(cstring_to_text(""));
 }
 
 GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 999ddad76d..7e3b52fbef 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -1347,7 +1347,7 @@ hstore_to_json_loose(PG_FUNCTION_ARGS)
 				dst;
 
 	if (count == 0)
-		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
+		PG_RETURN_TEXT_P(cstring_to_text("{}"));
 
 	initStringInfo(&tmp);
 	initStringInfo(&dst);
@@ -1402,7 +1402,7 @@ hstore_to_json(PG_FUNCTION_ARGS)
 				dst;
 
 	if (count == 0)
-		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
+		PG_RETURN_TEXT_P(cstring_to_text("{}"));
 
 	initStringInfo(&tmp);
 	initStringInfo(&dst);
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 2c620809b2..cd2b494259 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -1290,7 +1290,7 @@ json_build_object(PG_FUNCTION_ARGS)
 Datum
 json_build_object_noargs(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
+	PG_RETURN_TEXT_P(cstring_to_text("{}"));
 }
 
 Datum
@@ -1346,7 +1346,7 @@ json_build_array(PG_FUNCTION_ARGS)
 Datum
 json_build_array_noargs(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_TEXT_P(cstring_to_text_with_len("[]", 2));
+	PG_RETURN_TEXT_P(cstring_to_text("[]"));
 }
 
 /*
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index a4bfa5e404..b8845635ac 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1800,8 +1800,8 @@ JsonbValueAsText(JsonbValue *v)
 
 		case jbvBool:
 			return v->val.boolean ?
-				cstring_to_text_with_len("true", 4) :
-				cstring_to_text_with_len("false", 5);
+				cstring_to_text("true") :
+				cstring_to_text("false");
 
 		case jbvString:
 			return cstring_to_text_with_len(v->val.string.val,
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 72e1e24fe0..1529498e8b 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -171,36 +171,6 @@ static void text_format_append_string(StringInfo buf, const char *str,
  *	 CONVERSION ROUTINES EXPORTED FOR USE BY C CODE							 *
  *****************************************************************************/
 
-/*
- * cstring_to_text
- *
- * Create a text value from a null-terminated C string.
- *
- * The new text value is freshly palloc'd with a full-size VARHDR.
- */
-text *
-cstring_to_text(const char *s)
-{
-	return cstring_to_text_with_len(s, strlen(s));
-}
-
-/*
- * cstring_to_text_with_len
- *
- * Same as cstring_to_text except the caller specifies the string length;
- * the string need not be null_terminated.
- */
-text *
-cstring_to_text_with_len(const char *s, int len)
-{
-	text	   *result = (text *) palloc(len + VARHDRSZ);
-
-	SET_VARSIZE(result, len + VARHDRSZ);
-	memcpy(VARDATA(result), s, len);
-
-	return result;
-}
-
 /*
  * text_to_cstring
  *
@@ -4827,7 +4797,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
 
 	/* if there are no elements, return an empty string */
 	if (nitems == 0)
-		return cstring_to_text_with_len("", 0);
+		return cstring_to_text("");
 
 	element_type = ARR_ELEMTYPE(v);
 	initStringInfo(&buf);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 2f8b46d6da..198a88cb37 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -17,6 +17,7 @@
 #include "fmgr.h"
 #include "nodes/nodes.h"
 #include "utils/fmgrprotos.h"
+#include "varatt.h"
 
 /* Sign + the most decimal digits an 8-byte number could have */
 #define MAXINT8LEN 20
@@ -86,8 +87,37 @@ extern void generate_operator_clause(fmStringInfo buf,
 extern int	bpchartruelen(char *s, int len);
 
 /* popular functions from varlena.c */
-extern text *cstring_to_text(const char *s);
-extern text *cstring_to_text_with_len(const char *s, int len);
+
+/*
+ * cstring_to_text_with_len
+ *
+ * Same as cstring_to_text except the caller specifies the string length;
+ * the string need not be null-terminated.
+ */
+static inline text *
+cstring_to_text_with_len(const char *s, int len)
+{
+	text	   *result = (text *) palloc(len + VARHDRSZ);
+
+	SET_VARSIZE(result, len + VARHDRSZ);
+	memcpy(VARDATA(result), s, len);
+
+	return result;
+}
+
+/*
+ * cstring_to_text
+ *
+ * Create a text value from a null-terminated C string.
+ *
+ * The new text value is freshly palloc'd with a full-size VARHDR.
+ */
+static inline text *
+cstring_to_text(const char *s)
+{
+	return cstring_to_text_with_len(s, strlen(s));
+}
+
 extern char *text_to_cstring(const text *t);
 extern void text_to_cstring_buffer(const text *src, char *dst, size_t dst_len);
 
-- 
2.39.2