Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1icA0u-0001GO-2Y for pgsql-hackers@arkaria.postgresql.org; Tue, 03 Dec 2019 15:22:56 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1icA0s-0008Ts-Lg for pgsql-hackers@arkaria.postgresql.org; Tue, 03 Dec 2019 15:22:54 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1icA0r-0008Tl-W4 for pgsql-hackers@lists.postgresql.org; Tue, 03 Dec 2019 15:22:54 +0000 Received: from mail-qt1-x830.google.com ([2607:f8b0:4864:20::830]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1icA0l-0006dD-3U for pgsql-hackers@postgresql.org; Tue, 03 Dec 2019 15:22:52 +0000 Received: by mail-qt1-x830.google.com with SMTP id d5so4158358qto.0 for ; Tue, 03 Dec 2019 07:22:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :content-transfer-encoding:in-reply-to:user-agent; bh=aMtFpGlG9drA6kQNUJFr0p7XWKTlPFJTPGURFA8N1ss=; b=kUeDapZps7Z9QBvtmF6FYEs9bqktaVCxZHu0oLQy9qNBEWzd4gh5LGQM/NlNrf9QSA 5sB5Lu1EM+Q6VuPsdR1X4nLFqrm2IVIkE6qgmsKChzdnnCLEFhwMHP/kLgRzxVFp/x50 s5NogfiZzOZlDuUIN9847FSWg11rAcsCq7XSCzHaSa1m3mi2R/TTZWvjK/qu25xNP/xI PiWvku3cJ6MXfHSeqjsWKDHsVbDyKTR/Y2EBZWT+1JlQWN/1tHiwPbB82/6o56PilYx0 OL+/cHMnnwZCxRB5oV3xe4RbcWvPaBDIP4+xCon1CEH/zZJCdTiBkf16Kkiapky13lDx ah7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:content-transfer-encoding:in-reply-to :user-agent; bh=aMtFpGlG9drA6kQNUJFr0p7XWKTlPFJTPGURFA8N1ss=; b=UoiJFGuRY6WTZ+aKKcjuDDTch+17ifDo9f2bz+3VrKhtSFCnRIzIvZjTtxWLGvSuKM WF9smYuKipADjeG8cPPQETKSeg23elxu9IDuIE4CKBENKku2kWqkFpYVMSLwhleWgDa7 u7VNvJ4irdLymTHBbjnkyQh/ESszjgD/WSJ/S3mNkYRHxpnStUT6dcwVIzkfN0xOBSr7 MM3vcGei6iq16xBIlGKawfzD79/9YDCFUyUqx8BK4c/SxSnTw3Tbty4MzXTm5J68/SWW UkmKcLwL5uFEXWNMAz4JwQxWqklOUW/4E0n1GX458S8Qk1/rryGDcFnDhGDjI0WOAqjS KQ3A== X-Gm-Message-State: APjAAAUtA9R7aBM/KY2zvChRHdIq+tv1or81KHGtV/Gh5V67TyP42Dzz cpmJCts8/zo2+Hul6i4F+TGfjQ== X-Google-Smtp-Source: APXvYqxmnUnX4cw053SLW2Ofrzv4Ysd1jIC9xRMBqUZz7dVC62C8Lhe0YIPVI8bG6SAbAcHO5lG/Kw== X-Received: by 2002:ac8:37d0:: with SMTP id e16mr5459568qtc.311.1575386565747; Tue, 03 Dec 2019 07:22:45 -0800 (PST) Received: from nimloth.alvh.no-ip.org ([190.121.29.3]) by smtp.gmail.com with ESMTPSA id s2sm1839015qke.113.2019.12.03.07.22.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Dec 2019 07:22:44 -0800 (PST) Received: by nimloth.alvh.no-ip.org (Postfix, from userid 1000) id E8C9B300801; Tue, 3 Dec 2019 12:22:40 -0300 (-03) Date: Tue, 3 Dec 2019 12:22:40 -0300 From: Alvaro Herrera To: Andres Freund Cc: Alexey Bashtanov , Peter Eisentraut , PostgreSQL Hackers Subject: Re: log bind parameter values on error Message-ID: <20191203152240.GA21110@alvherre.pgsql> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="AqsLC8rIMeq19msA" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20190920203905.xkv5udsd5dxfs6tr@alap3.anarazel.de> User-Agent: Mutt/1.10.1 (2018-07-13) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --AqsLC8rIMeq19msA Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit On 2019-Sep-20, Andres Freund wrote: > > > > + appendStringInfoCharMacro(¶m_str, '\''); > > > > + for (p = pstring; *p; p++) > > > > + { > > > > + if (*p == '\'') /* double single quotes */ > > > > + appendStringInfoCharMacro(¶m_str, *p); > > > > + appendStringInfoCharMacro(¶m_str, *p); > > > > + } > > > > + appendStringInfoCharMacro(¶m_str, '\''); > > > > > > I know this is old code, but: This is really inefficient. Will cause a > > > lot of unnecessary memory-reallocations for large text outputs, because > > > we don't immediately grow to at least close to the required size. > I'd probably just count the ' in one pass, enlarge the stringinfo to the > required size, and then put the string directly into he stringbuffer. > Possibly just putting the necessary code into stringinfo.c. We already > have multiple copies of this inefficient logic... I stole Alexey's code for this from downthread and tried to apply to all these places. However, I did not put it to use in all these places you mention, because each of them has slightly different requirements; details below. Now, I have to say that this doesn't make me terribly happy, because I would like the additional ability to limit the printed values to N bytes. This means the new function would have to have an additional argument to indicate the maximum length (pass 0 to print all args whole) ... and the logic then because more involved because we need logic to stop printing early. I think the current callers should all use the early termination logic; having plpgsql potentially produce 1 MB of log output because of a long argument seems silly. So I see no use for this function *without* the length-limiting logic. > But even if not, I don't think writing data into the stringbuf directly > is that ugly, we do that in enough places that I'd argue that that's > basically part of how it's expected to be used. > > In HEAD there's at least > - postgres.c:errdetail_params(), > - pl_exec.c:format_preparedparamsdata() > pl_exec.c:format_expr_params() These are changed in the patch; they're all alike. > - dblink.c:escape_param_str() This one wants to use \' and \\ instead of just doubling each '. The resulting string is used as libpq conninfo, so using doubled ' does not work. > - deparse.c:deparseStringLiteral() This one wants to use E''-style strings that ignore std_conforming_strings; the logic would need to detect two chars ' and \ instead of just ' so we'd need to use strcspn instead of strchr(); that seems a lot slower. > - xlog.c:do_pg_start_backup() (after "Add the escape character"), This one wants to escape \n chars. > - tsearchcmds.c:serialize_deflist() This wants E''-style strings, like deparse.c. > - ruleutils.c:simple_quote_literal() Produce an escaped string according to the prevailing std_conforming_strings. I think it's possible to produce a function that would satisfy all of these, but it would be another function similar to the one I'm writing here, not the same one. -- Álvaro Herrera https://www.2ndQuadrant.com/ ggPostgreSQL Development, 24x7 Support, Remote DBA, Training & Services --AqsLC8rIMeq19msA Content-Type: text/x-diff; charset=iso-8859-1 Content-Disposition: attachment; filename="0001-Add-appendStringInfoStringQuoted.patch" Content-Transfer-Encoding: 8bit From 99cf1d122bc935a4060c6b359fb2c262035039df Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Tue, 3 Dec 2019 10:08:35 -0300 Subject: [PATCH] Add appendStringInfoStringQuoted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplifies some coding that prints parameters, as well as optimize to do it per non-quote chunks instead of per byte. Author: Alexey Bashtanov and Álvaro Herrera, after a suggestion from Andres Freund Discussion: https://postgr.es/m/20190920203905.xkv5udsd5dxfs6tr@alap3.anarazel.de --- src/backend/tcop/postgres.c | 10 +-------- src/common/stringinfo.c | 40 ++++++++++++++++++++++++++++++++++++ src/include/lib/stringinfo.h | 7 +++++++ src/pl/plpgsql/src/pl_exec.c | 34 ++++++++---------------------- 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 3b85e48333..0bff20ad67 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -2348,7 +2348,6 @@ errdetail_params(ParamListInfo params) Oid typoutput; bool typisvarlena; char *pstring; - char *p; appendStringInfo(¶m_str, "%s$%d = ", paramno > 0 ? ", " : "", @@ -2364,14 +2363,7 @@ errdetail_params(ParamListInfo params) pstring = OidOutputFunctionCall(typoutput, prm->value); - appendStringInfoCharMacro(¶m_str, '\''); - for (p = pstring; *p; p++) - { - if (*p == '\'') /* double single quotes */ - appendStringInfoCharMacro(¶m_str, *p); - appendStringInfoCharMacro(¶m_str, *p); - } - appendStringInfoCharMacro(¶m_str, '\''); + appendStringInfoStringQuoted(¶m_str, pstring); pfree(pstring); } diff --git a/src/common/stringinfo.c b/src/common/stringinfo.c index a50e587da9..f4dddf8223 100644 --- a/src/common/stringinfo.c +++ b/src/common/stringinfo.c @@ -178,6 +178,46 @@ appendStringInfoString(StringInfo str, const char *s) appendBinaryStringInfo(str, s, strlen(s)); } +/* + * appendStringInfoStringQuoted + * + * Append a null-terminated string to str, adding single quotes around it + * and doubling all single quotes. + */ +void +appendStringInfoStringQuoted(StringInfo str, const char *s) +{ + const char *chunk_search_start = s, + *chunk_copy_start = s, + *chunk_end; + int len; + + Assert(str != NULL); + + 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, ensuring sufficient space for final bytes */ + len = strlen(chunk_copy_start); + enlargeStringInfo(str, len + 2); + appendBinaryStringInfoNT(str, chunk_copy_start, len); + + /* add terminators */ + appendStringInfoCharMacro(str, '\''); + str->data[str->len] = '\0'; +} + /* * appendStringInfoChar * diff --git a/src/include/lib/stringinfo.h b/src/include/lib/stringinfo.h index e27942728e..7de2773e1f 100644 --- a/src/include/lib/stringinfo.h +++ b/src/include/lib/stringinfo.h @@ -113,6 +113,13 @@ extern int appendStringInfoVA(StringInfo str, const char *fmt, va_list args) pg_ */ extern void appendStringInfoString(StringInfo str, const char *s); +/*------------------------ + * appendStringInfoStringQuoted + * 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); + /*------------------------ * appendStringInfoChar * Append a single byte to str. diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 4f0de7a811..23553a6948 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -8611,19 +8611,10 @@ format_expr_params(PLpgSQL_execstate *estate, if (paramisnull) appendStringInfoString(¶mstr, "NULL"); else - { - char *value = convert_value_to_string(estate, paramdatum, paramtypeid); - char *p; - - appendStringInfoCharMacro(¶mstr, '\''); - for (p = value; *p; p++) - { - if (*p == '\'') /* double single quotes */ - appendStringInfoCharMacro(¶mstr, *p); - appendStringInfoCharMacro(¶mstr, *p); - } - appendStringInfoCharMacro(¶mstr, '\''); - } + appendStringInfoStringQuoted(¶mstr, + convert_value_to_string(estate, + paramdatum, + paramtypeid)); paramno++; } @@ -8661,19 +8652,10 @@ format_preparedparamsdata(PLpgSQL_execstate *estate, if (ppd->nulls[paramno] == 'n') appendStringInfoString(¶mstr, "NULL"); else - { - char *value = convert_value_to_string(estate, ppd->values[paramno], ppd->types[paramno]); - char *p; - - appendStringInfoCharMacro(¶mstr, '\''); - for (p = value; *p; p++) - { - if (*p == '\'') /* double single quotes */ - appendStringInfoCharMacro(¶mstr, *p); - appendStringInfoCharMacro(¶mstr, *p); - } - appendStringInfoCharMacro(¶mstr, '\''); - } + appendStringInfoStringQuoted(¶mstr, + convert_value_to_string(estate, + ppd->values[paramno], + ppd->types[paramno])); } MemoryContextSwitchTo(oldcontext); -- 2.20.1 --AqsLC8rIMeq19msA--