Add backend-only appendStringInfoStringQuoted

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.xkv5udsd5dxfs6tr@alap3.anarazel.de
This commit is contained in:
Alvaro Herrera 2019-12-10 17:09:32 -03:00
parent 0da33c762b
commit 6cafde1bd4
8 changed files with 152 additions and 35 deletions

View 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);
}

View File

@ -16,6 +16,7 @@ OBJS = \
conv.o \
encnames.o \
mbutils.o \
stringinfo_mb.o \
wchar.o \
wstrcmp.o \
wstrncmp.o

View 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

View File

@ -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);
}

View File

@ -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 */

View 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);

View 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

View 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;