diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index ce868292100ec057a965d51b2b21b704942baf0c..30f1b0eab02b1555e6695600a6cc313c0ac4b5d5 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -91,7 +91,6 @@ static char **get_text_array_contents(ArrayType *array, int *numitems);
 static char *get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals);
 static char *get_sql_delete(Relation rel, int *pkattnums, int pknumatts, char **tgt_pkattvals);
 static char *get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals);
-static char *quote_literal_cstr(char *rawstr);
 static char *quote_ident_cstr(char *rawstr);
 static int	get_attnum_pk_pos(int *pkattnums, int pknumatts, int key);
 static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals);
@@ -1893,25 +1892,6 @@ get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals
 	return (buf.data);
 }
 
-/*
- * Return a properly quoted literal value.
- * Uses quote_literal in quote.c
- */
-static char *
-quote_literal_cstr(char *rawstr)
-{
-	text	   *rawstr_text;
-	text	   *result_text;
-	char	   *result;
-
-	rawstr_text = cstring_to_text(rawstr);
-	result_text = DatumGetTextP(DirectFunctionCall1(quote_literal,
-											  PointerGetDatum(rawstr_text)));
-	result = text_to_cstring(result_text);
-
-	return result;
-}
-
 /*
  * Return a properly quoted identifier.
  * Uses quote_ident in quote.c
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index ea2f2e15b0a3428a1dba73f93d10021a5618aadf..2e9ea1a626ef5a1ffc0af2be5cc234cb5a0c15c4 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -85,7 +85,6 @@ static Tuplestorestate *build_tuplestore_recursively(char *key_fld,
 							 MemoryContext per_query_ctx,
 							 AttInMetadata *attinmeta,
 							 Tuplestorestate *tupstore);
-static char *quote_literal_cstr(char *rawstr);
 
 typedef struct
 {
@@ -1564,22 +1563,3 @@ compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
 	/* OK, the two tupdescs are compatible for our purposes */
 	return true;
 }
-
-/*
- * Return a properly quoted literal value.
- * Uses quote_literal in quote.c
- */
-static char *
-quote_literal_cstr(char *rawstr)
-{
-	text	   *rawstr_text;
-	text	   *result_text;
-	char	   *result;
-
-	rawstr_text = cstring_to_text(rawstr);
-	result_text = DatumGetTextP(DirectFunctionCall1(quote_literal,
-											  PointerGetDatum(rawstr_text)));
-	result = text_to_cstring(result_text);
-
-	return result;
-}
diff --git a/src/backend/utils/adt/quote.c b/src/backend/utils/adt/quote.c
index 70e98cad84b09be0d6ca7e5cac37bb0d3ad60398..af07443f7df065cc9dc938123a92d8615a6a7de4 100644
--- a/src/backend/utils/adt/quote.c
+++ b/src/backend/utils/adt/quote.c
@@ -33,8 +33,8 @@ quote_ident(PG_FUNCTION_ARGS)
 }
 
 /*
- * quote_literal -
- *	  returns a properly quoted literal
+ * quote_literal_internal -
+ *	  helper function for quote_literal and quote_literal_cstr
  *
  * NOTE: think not to make this function's behavior change with
  * standard_conforming_strings.  We don't know where the result
@@ -42,6 +42,37 @@ quote_ident(PG_FUNCTION_ARGS)
  * will work with either setting.  Take a look at what dblink
  * uses this for before thinking you know better.
  */
+static size_t
+quote_literal_internal(char *dst, char *src, size_t len)
+{
+	char	   *s;
+	char	   *savedst = dst;
+
+	for (s = src; s < src + len; s++)
+	{
+		if (*s == '\\')
+		{
+			*dst++ = ESCAPE_STRING_SYNTAX;
+			break;
+		}
+	}
+
+	*dst++ = '\'';
+	while (len-- > 0)
+	{
+		if (SQL_STR_DOUBLE(*src, true))
+			*dst++ = *src;
+		*dst++ = *src++;
+	}
+	*dst++ = '\'';
+
+	return dst - savedst;
+}
+
+/*
+ * quote_literal -
+ *	  returns a properly quoted literal
+ */
 Datum
 quote_literal(PG_FUNCTION_ARGS)
 {
@@ -58,30 +89,30 @@ quote_literal(PG_FUNCTION_ARGS)
 	cp1 = VARDATA(t);
 	cp2 = VARDATA(result);
 
-	for (; len-- > 0; cp1++)
-	{
-		if (*cp1 == '\\')
-		{
-			*cp2++ = ESCAPE_STRING_SYNTAX;
-			break;
-		}
-	}
+	SET_VARSIZE(result, VARHDRSZ + quote_literal_internal(cp2, cp1, len));
 
-	len = VARSIZE(t) - VARHDRSZ;
-	cp1 = VARDATA(t);
+	PG_RETURN_TEXT_P(result);
+}
 
-	*cp2++ = '\'';
-	while (len-- > 0)
-	{
-		if (SQL_STR_DOUBLE(*cp1, true))
-			*cp2++ = *cp1;
-		*cp2++ = *cp1++;
-	}
-	*cp2++ = '\'';
+/*
+ * quote_literal_cstr -
+ *	  returns a properly quoted literal
+ */
+char *
+quote_literal_cstr(char *rawstr)
+{
+	char	   *result;
+	int			len;
+	int			newlen;
 
-	SET_VARSIZE(result, cp2 - ((char *) result));
+	len = strlen(rawstr);
+	/* We make a worst-case result area; wasting a little space is OK */
+	result = palloc(len * 2 + 3);
 
-	PG_RETURN_TEXT_P(result);
+	newlen = quote_literal_internal(result, rawstr, len);
+	result[newlen] = '\0';
+
+	return result;
 }
 
 /*
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7b1bb2353834337dcc53cdcf4528b34aaf351d0e..ae267ab17ad34945dd96f1756ce8abe2372a6eab 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -977,6 +977,7 @@ extern int32 type_maximum_size(Oid type_oid, int32 typemod);
 /* quote.c */
 extern Datum quote_ident(PG_FUNCTION_ARGS);
 extern Datum quote_literal(PG_FUNCTION_ARGS);
+extern char *quote_literal_cstr(char *rawstr);
 extern Datum quote_nullable(PG_FUNCTION_ARGS);
 
 /* guc.c */
diff --git a/src/test/regress/expected/text.out b/src/test/regress/expected/text.out
index 84f4a5cda8b79bd033f76b6288228e220c4d74fd..0b0014a6e25ffe0246357e2d66b60434ee180968 100644
--- a/src/test/regress/expected/text.out
+++ b/src/test/regress/expected/text.out
@@ -118,3 +118,21 @@ select i, left('ahoj', i), right('ahoj', i) from generate_series(-5, 5) t(i) ord
   5 | ahoj | ahoj
 (11 rows)
 
+select quote_literal('');
+ quote_literal 
+---------------
+ ''
+(1 row)
+
+select quote_literal('abc''');
+ quote_literal 
+---------------
+ 'abc'''
+(1 row)
+
+select quote_literal(e'\\');
+ quote_literal 
+---------------
+ E'\\'
+(1 row)
+
diff --git a/src/test/regress/sql/text.sql b/src/test/regress/sql/text.sql
index a8768ee81aa5526112a22e843063f6f954312363..50c3033d9eb65e82996f628e16643fb6f1760189 100644
--- a/src/test/regress/sql/text.sql
+++ b/src/test/regress/sql/text.sql
@@ -41,3 +41,6 @@ select concat_ws('',10,20,null,30);
 select concat_ws(NULL,10,20,null,30) is null;
 select reverse('abcde');
 select i, left('ahoj', i), right('ahoj', i) from generate_series(-5, 5) t(i) order by i;
+select quote_literal('');
+select quote_literal('abc''');
+select quote_literal(e'\\');