Add backend-only appendStringInfoStringQuoted
authorAlvaro Herrera <[email protected]>
Tue, 10 Dec 2019 20:09:32 +0000 (17:09 -0300)
committerAlvaro Herrera <[email protected]>
Tue, 10 Dec 2019 20:12:56 +0000 (17:12 -0300)
This provides a mechanism to emit literal values in informative
messages, such as query parameters.  The new code is more complex than
what it replaces, primarily because it wants to be more efficient.
It also has the (currently unused) additional optional capability of
specifying a maximum size to print.

The new function lives out of common/stringinfo.c so that frontend users
of that file need not pull in unnecessary multibyte-encoding support
code.

Author: Álvaro Herrera and Alexey Bashtanov, after a suggestion from Andres Freund
Reviewed-by: Tom Lane
Discussion: https://postgr.es/m/20190920203905[email protected]

src/backend/tcop/postgres.c
src/backend/utils/mb/Makefile
src/backend/utils/mb/README
src/backend/utils/mb/stringinfo_mb.c [new file with mode: 0644]
src/include/mb/stringinfo_mb.h [new file with mode: 0644]
src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/plpgsql.out
src/test/regress/sql/plpgsql.sql

index 3b85e48333d9a1f03f79be52bcb142b3fe3cea90..512209a38c40bb2a90711e5fdaa6eadb86466eae 100644 (file)
@@ -48,6 +48,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
 #include "mb/pg_wchar.h"
+#include "mb/stringinfo_mb.h"
 #include "miscadmin.h"
 #include "nodes/print.h"
 #include "optimizer/optimizer.h"
@@ -2348,7 +2349,6 @@ errdetail_params(ParamListInfo params)
                        Oid                     typoutput;
                        bool            typisvarlena;
                        char       *pstring;
-                       char       *p;
 
                        appendStringInfo(&param_str, "%s$%d = ",
                                                         paramno > 0 ? ", " : "",
@@ -2364,14 +2364,7 @@ errdetail_params(ParamListInfo params)
 
                        pstring = OidOutputFunctionCall(typoutput, prm->value);
 
-                       appendStringInfoCharMacro(&param_str, '\'');
-                       for (p = pstring; *p; p++)
-                       {
-                               if (*p == '\'') /* double single quotes */
-                                       appendStringInfoCharMacro(&param_str, *p);
-                               appendStringInfoCharMacro(&param_str, *p);
-                       }
-                       appendStringInfoCharMacro(&param_str, '\'');
+                       appendStringInfoStringQuoted(&param_str, pstring, 0);
 
                        pfree(pstring);
                }
index 18dd758cfe848796b365a961a32c2f6b0f59aca2..cd4a016449e8e38ce7e8ca2815d30e36bb381917 100644 (file)
@@ -16,6 +16,7 @@ OBJS = \
        conv.o \
        encnames.o \
        mbutils.o \
+       stringinfo_mb.o \
        wchar.o \
        wstrcmp.o \
        wstrncmp.o
index c9bc6e6f8d63e79aa24c7d25aef5a65cb9635393..7495ca5db2382e451fbba73de5ee8a203e2e881c 100644 (file)
@@ -9,6 +9,7 @@ wchar.c:        mostly static functions and a public table for mb string and
                multibyte conversion
 mbutils.c:     public functions for the backend only.
                requires conv.c and wchar.c
+stringinfo_mb.c: public backend-only multibyte-aware stringinfo functions
 wstrcmp.c:     strcmp for mb
 wstrncmp.c:    strncmp for mb
 win866.c:      a tool to generate KOI8 <--> CP866 conversion table
diff --git a/src/backend/utils/mb/stringinfo_mb.c b/src/backend/utils/mb/stringinfo_mb.c
new file mode 100644 (file)
index 0000000..51bb051
--- /dev/null
@@ -0,0 +1,86 @@
+/*-------------------------------------------------------------------------
+ *
+ * stringinfo_mb.c
+ *             Multibyte encoding-aware additional StringInfo facilites
+ *
+ * This is separate from common/stringinfo.c so that frontend users
+ * of that file need not pull in unnecessary multibyte-encoding support
+ * code.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/utils/mb/stringinfo_mb.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "mb/stringinfo_mb.h"
+#include "mb/pg_wchar.h"
+
+
+/*
+ * appendStringInfoStringQuoted
+ *
+ * Append up to maxlen characters from s to str, or the whole input string if
+ * maxlen <= 0, adding single quotes around it and doubling all single quotes.
+ * Add an ellipsis if the copy is incomplete.
+ */
+void
+appendStringInfoStringQuoted(StringInfo str, const char *s, int maxlen)
+{
+       char       *copy = NULL;
+       const char *chunk_search_start,
+                          *chunk_copy_start,
+                          *chunk_end;
+       int                     slen;
+       bool            ellipsis;
+
+       Assert(str != NULL);
+
+       slen = strlen(s);
+       if (maxlen > 0 && maxlen < slen)
+       {
+               int             finallen = pg_mbcliplen(s, slen, maxlen);
+
+               copy = pnstrdup(s, finallen);
+               chunk_search_start = copy;
+               chunk_copy_start = copy;
+
+               ellipsis = true;
+       }
+       else
+       {
+               chunk_search_start = s;
+               chunk_copy_start = s;
+
+               ellipsis = false;
+       }
+
+       appendStringInfoCharMacro(str, '\'');
+
+       while ((chunk_end = strchr(chunk_search_start, '\'')) != NULL)
+       {
+               /* copy including the found delimiting ' */
+               appendBinaryStringInfoNT(str,
+                                                                chunk_copy_start,
+                                                                chunk_end - chunk_copy_start + 1);
+
+               /* in order to double it, include this ' into the next chunk as well */
+               chunk_copy_start = chunk_end;
+               chunk_search_start = chunk_end + 1;
+       }
+
+       /* copy the last chunk and terminate */
+       if (ellipsis)
+               appendStringInfo(str, "%s...'", chunk_copy_start);
+       else
+               appendStringInfo(str, "%s'", chunk_copy_start);
+
+       if (copy)
+               pfree(copy);
+}
diff --git a/src/include/mb/stringinfo_mb.h b/src/include/mb/stringinfo_mb.h
new file mode 100644 (file)
index 0000000..e90ba8b
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * stringinfo_mb.h
+ *       multibyte support for StringInfo
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/mb/stringinfo_mb.h
+ *-------------------------------------------------------------------------
+ */
+#ifndef STRINGINFO_MB_H
+#define STRINGINFO_MB_H
+
+
+#include "lib/stringinfo.h"
+
+/*
+ * Multibyte-aware StringInfo support function.
+ */
+extern void appendStringInfoStringQuoted(StringInfo str,
+                                                                                const char *s, int maxlen);
+
+#endif                                                 /* STRINGINFO_MB_H */
index 4f0de7a8112ef8a3ca226accbdfe936989df2d0e..2deb7c0b12ac66977fc1256ef21671e309fda39d 100644 (file)
@@ -28,6 +28,7 @@
 #include "executor/spi.h"
 #include "executor/spi_priv.h"
 #include "funcapi.h"
+#include "mb/stringinfo_mb.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/optimizer.h"
@@ -8611,19 +8612,11 @@ format_expr_params(PLpgSQL_execstate *estate,
                if (paramisnull)
                        appendStringInfoString(&paramstr, "NULL");
                else
-               {
-                       char       *value = convert_value_to_string(estate, paramdatum, paramtypeid);
-                       char       *p;
-
-                       appendStringInfoCharMacro(&paramstr, '\'');
-                       for (p = value; *p; p++)
-                       {
-                               if (*p == '\'') /* double single quotes */
-                                       appendStringInfoCharMacro(&paramstr, *p);
-                               appendStringInfoCharMacro(&paramstr, *p);
-                       }
-                       appendStringInfoCharMacro(&paramstr, '\'');
-               }
+                       appendStringInfoStringQuoted(&paramstr,
+                                                                                convert_value_to_string(estate,
+                                                                                                                                paramdatum,
+                                                                                                                                paramtypeid),
+                                                                                0);
 
                paramno++;
        }
@@ -8661,19 +8654,11 @@ format_preparedparamsdata(PLpgSQL_execstate *estate,
                if (ppd->nulls[paramno] == 'n')
                        appendStringInfoString(&paramstr, "NULL");
                else
-               {
-                       char       *value = convert_value_to_string(estate, ppd->values[paramno], ppd->types[paramno]);
-                       char       *p;
-
-                       appendStringInfoCharMacro(&paramstr, '\'');
-                       for (p = value; *p; p++)
-                       {
-                               if (*p == '\'') /* double single quotes */
-                                       appendStringInfoCharMacro(&paramstr, *p);
-                               appendStringInfoCharMacro(&paramstr, *p);
-                       }
-                       appendStringInfoCharMacro(&paramstr, '\'');
-               }
+                       appendStringInfoStringQuoted(&paramstr,
+                                                                                convert_value_to_string(estate,
+                                                                                                                                ppd->values[paramno],
+                                                                                                                                ppd->types[paramno]),
+                                                                                0);
        }
 
        MemoryContextSwitchTo(oldcontext);
index e85b29455e5da69da6ecd8e7dc7fc4c9bcf6c16b..cd2c79f4d5094e7bbef8d10db8ea33e8b4bf433b 100644 (file)
@@ -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 mirase bien lo que hacia?'''
+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
index 70deadfbea29b70c3115d4f181503d4bb273dfe9..d841d8c0f977eccbc9246ab1a87e68355d336027 100644 (file)
@@ -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;