From a34b4728658891cb7d37433877efd0b706331dd3 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Tue, 3 Dec 2019 18:12:25 -0300
Subject: [PATCH v3 3/3] Restrict length printed by
 appendStringInfoStringQuoted

---
 src/backend/tcop/postgres.c           |  2 +-
 src/common/stringinfo.c               | 49 +++++++++++++++++++++------
 src/include/lib/stringinfo.h          |  3 +-
 src/pl/plpgsql/src/pl_exec.c          |  6 ++--
 src/test/regress/expected/plpgsql.out | 14 ++++++++
 src/test/regress/sql/plpgsql.sql      | 13 +++++++
 6 files changed, 72 insertions(+), 15 deletions(-)

diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0bff20ad67..c38ea2c743 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -2363,7 +2363,7 @@ errdetail_params(ParamListInfo params)
 
 			pstring = OidOutputFunctionCall(typoutput, prm->value);
 
-			appendStringInfoStringQuoted(&param_str, pstring);
+			appendStringInfoStringQuoted(&param_str, pstring, 64);
 
 			pfree(pstring);
 		}
diff --git a/src/common/stringinfo.c b/src/common/stringinfo.c
index f4dddf8223..6f7555ef96 100644
--- a/src/common/stringinfo.c
+++ b/src/common/stringinfo.c
@@ -185,15 +185,34 @@ appendStringInfoString(StringInfo str, const char *s)
  * and doubling all single quotes.
  */
 void
-appendStringInfoStringQuoted(StringInfo str, const char *s)
+appendStringInfoStringQuoted(StringInfo str, const char *s, int maxlen)
 {
-	const char *chunk_search_start = s,
-			   *chunk_copy_start = s,
+	char	   *copy = NULL;
+	const char *chunk_search_start,
+			   *chunk_copy_start,
 			   *chunk_end;
-	int			len;
+	bool		ellipsis;
 
 	Assert(str != NULL);
 
+	if (maxlen > 0)
+	{
+		copy = pnstrdup(s, maxlen);
+		chunk_search_start = copy;
+		chunk_copy_start = copy;
+
+		ellipsis = strnlen(s, maxlen + 1) > maxlen;
+		/* enlarge while we can do so cheaply */
+		enlargeStringInfo(str, maxlen);
+	}
+	else
+	{
+		chunk_search_start = s;
+		chunk_copy_start = s;
+
+		ellipsis = false;
+	}
+
 	appendStringInfoCharMacro(str, '\'');
 
 	while ((chunk_end = strchr(chunk_search_start, '\'')) != NULL)
@@ -208,14 +227,22 @@ appendStringInfoStringQuoted(StringInfo str, const char *s)
 		chunk_search_start = chunk_end + 1;
 	}
 
-	/* copy the last chunk, ensuring sufficient space for final bytes */
-	len = strlen(chunk_copy_start);
-	enlargeStringInfo(str, len + 2);
-	appendBinaryStringInfoNT(str, chunk_copy_start, len);
+	/* copy the last chunk and terminate */
+	if (ellipsis)
+		appendStringInfo(str, "%s...'", chunk_copy_start);
+	else
+	{
+		int		len = strlen(chunk_copy_start);
 
-	/* add terminators */
-	appendStringInfoCharMacro(str, '\'');
-	str->data[str->len] = '\0';
+		/* ensure sufficient space for terminators */
+		enlargeStringInfo(str, len + 2);
+		appendBinaryStringInfoNT(str, chunk_copy_start, len);
+		appendStringInfoCharMacro(str, '\'');
+		str->data[str->len] = '\0';
+	}
+
+	if (copy)
+		pfree(copy);
 }
 
 /*
diff --git a/src/include/lib/stringinfo.h b/src/include/lib/stringinfo.h
index 7de2773e1f..956ccbba45 100644
--- a/src/include/lib/stringinfo.h
+++ b/src/include/lib/stringinfo.h
@@ -118,7 +118,8 @@ extern void appendStringInfoString(StringInfo str, const char *s);
  * Append a null-terminated string to str, adding single quotes around it
  * and doubling all single quotes.
  */
-extern void appendStringInfoStringQuoted(StringInfo str, const char *s);
+extern void appendStringInfoStringQuoted(StringInfo str, const char *s,
+										 int maxlen);
 
 /*------------------------
  * appendStringInfoChar
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 23553a6948..752a66f379 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8614,7 +8614,8 @@ format_expr_params(PLpgSQL_execstate *estate,
 			appendStringInfoStringQuoted(&paramstr,
 										 convert_value_to_string(estate,
 																 paramdatum,
-																 paramtypeid));
+																 paramtypeid),
+										 64);
 
 		paramno++;
 	}
@@ -8655,7 +8656,8 @@ format_preparedparamsdata(PLpgSQL_execstate *estate,
 			appendStringInfoStringQuoted(&paramstr,
 										 convert_value_to_string(estate,
 																 ppd->values[paramno],
-																 ppd->types[paramno]));
+																 ppd->types[paramno]),
+										 64);
 	}
 
 	MemoryContextSwitchTo(oldcontext);
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index e85b29455e..a53ae28548 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -2656,6 +2656,20 @@ create or replace function stricttest() returns void as $$
 declare
 x record;
 p1 int := 2;
+p3 text := $a$'Valame Dios!' dijo Sancho; 'no le dije yo a vuestra merced que mirase bien lo que hacia?'$a$;
+begin
+  -- no rows
+  select * from foo where f1 = p1 and f1::text = p3 into strict x;
+  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
+end$$ language plpgsql;
+select stricttest();
+ERROR:  query returned no rows
+DETAIL:  parameters: p1 = '2', p3 = '''Valame Dios!'' dijo Sancho; ''no le dije yo a vuestra merced que ...'
+CONTEXT:  PL/pgSQL function stricttest() line 8 at SQL statement
+create or replace function stricttest() returns void as $$
+declare
+x record;
+p1 int := 2;
 p3 text := 'foo';
 begin
   -- too many rows
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 70deadfbea..d841d8c0f9 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -2280,6 +2280,19 @@ end$$ language plpgsql;
 
 select stricttest();
 
+create or replace function stricttest() returns void as $$
+declare
+x record;
+p1 int := 2;
+p3 text := $a$'Valame Dios!' dijo Sancho; 'no le dije yo a vuestra merced que mirase bien lo que hacia?'$a$;
+begin
+  -- no rows
+  select * from foo where f1 = p1 and f1::text = p3 into strict x;
+  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
+end$$ language plpgsql;
+
+select stricttest();
+
 create or replace function stricttest() returns void as $$
 declare
 x record;
-- 
2.20.1

