Make StringInfo available to frontend code.

There's plenty places in frontend code that could benefit from a
string buffer implementation. Some because it yields simpler and
faster code, and some others because of the desire to share code
between backend and frontend.

While there is a string buffer implementation available to frontend
code, libpq's PQExpBuffer, it is clunkier than stringinfo, it
introduces a libpq dependency, doesn't allow for sharing between
frontend and backend code, and has a higher API/ABI stability
requirement due to being exposed via libpq.

Therefore it seems best to just making StringInfo being usable by
frontend code. There's not much to do for that, except for rewriting
two subsequent elog/ereport calls into others types of error
reporting, and deciding on a maximum string length.

For the maximum string size I decided to privately define MaxAllocSize
to the same value as used in the backend. It seems likely that we'll
want to reconsider this for both backend and frontend code in the not
too far away future.

For now I've left stringinfo.h in lib/, rather than common/, to reduce
the likelihood of unnecessary breakage. We could alternatively decide
to provide a redirecting stringinfo.h in lib/, or just not provide
compatibility.

Author: Andres Freund
Reviewed-By: Kyotaro Horiguchi, Daniel Gustafsson
Discussion: https://postgr.es/m/20190920051857.2fhnvhvx4qdddviz@alap3.anarazel.de
This commit is contained in:
Andres Freund 2019-11-05 14:56:40 -08:00
parent 01368e5d9d
commit 26aaf97b68
7 changed files with 49 additions and 43 deletions

View File

@ -23,6 +23,5 @@ OBJS = \
knapsack.o \ knapsack.o \
pairingheap.o \ pairingheap.o \
rbtree.o \ rbtree.o \
stringinfo.o
include $(top_srcdir)/src/backend/common.mk include $(top_srcdir)/src/backend/common.mk

View File

@ -20,7 +20,6 @@
#include <time.h> #include <time.h>
#include "lib/stringinfo.h"
#include "utils/datetime.h" #include "utils/datetime.h"
/* copied from timestamp.c */ /* copied from timestamp.c */
@ -63,29 +62,3 @@ timestamptz_to_str(TimestampTz dt)
return buf; return buf;
} }
/*
* Provide a hacked up compat layer for StringInfos so xlog desc functions can
* be linked/called.
*/
void
appendStringInfo(StringInfo str, const char *fmt,...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
void
appendStringInfoString(StringInfo str, const char *string)
{
appendStringInfo(str, "%s", string);
}
void
appendStringInfoChar(StringInfo str, char ch)
{
appendStringInfo(str, "%c", ch);
}

View File

@ -514,6 +514,7 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
int block_id; int block_id;
uint8 info = XLogRecGetInfo(record); uint8 info = XLogRecGetInfo(record);
XLogRecPtr xl_prev = XLogRecGetPrev(record); XLogRecPtr xl_prev = XLogRecGetPrev(record);
StringInfoData s;
XLogDumpRecordLen(record, &rec_len, &fpi_len); XLogDumpRecordLen(record, &rec_len, &fpi_len);
@ -530,8 +531,10 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
else else
printf("desc: %s ", id); printf("desc: %s ", id);
/* the desc routine will printf the description directly to stdout */ initStringInfo(&s);
desc->rm_desc(NULL, record); desc->rm_desc(&s, record);
printf("%s", s.data);
pfree(s.data);
if (!config->bkp_details) if (!config->bkp_details)
{ {

View File

@ -67,6 +67,7 @@ OBJS_COMMON = \
saslprep.o \ saslprep.o \
scram-common.o \ scram-common.o \
string.o \ string.o \
stringinfo.o \
unicode_norm.o \ unicode_norm.o \
username.o \ username.o \
wait_error.o wait_error.o

View File

@ -2,21 +2,34 @@
* *
* stringinfo.c * stringinfo.c
* *
* StringInfo provides an indefinitely-extensible string data type. * StringInfo provides an extensible string data type (currently limited to a
* It can be used to buffer either ordinary C strings (null-terminated text) * length of 1GB). It can be used to buffer either ordinary C strings
* or arbitrary binary data. All storage is allocated with palloc(). * (null-terminated text) or arbitrary binary data. All storage is allocated
* with palloc() (falling back to malloc in frontend code).
* *
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* src/backend/lib/stringinfo.c * src/common/stringinfo.c
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#ifndef FRONTEND
#include "postgres.h" #include "postgres.h"
#include "utils/memutils.h"
#else
#include "postgres_fe.h"
/* It's possible we could use a different value for this in frontend code */
#define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
#endif
#include "lib/stringinfo.h" #include "lib/stringinfo.h"
#include "utils/memutils.h"
/* /*
@ -261,10 +274,10 @@ appendBinaryStringInfoNT(StringInfo str, const char *data, int datalen)
* can save some palloc overhead by enlarging the buffer before starting * can save some palloc overhead by enlarging the buffer before starting
* to store data in it. * to store data in it.
* *
* NB: because we use repalloc() to enlarge the buffer, the string buffer * NB: In the backend, because we use repalloc() to enlarge the buffer, the
* will remain allocated in the same memory context that was current when * string buffer will remain allocated in the same memory context that was
* initStringInfo was called, even if another context is now current. * current when initStringInfo was called, even if another context is now
* This is the desired and indeed critical behavior! * current. This is the desired and indeed critical behavior!
*/ */
void void
enlargeStringInfo(StringInfo str, int needed) enlargeStringInfo(StringInfo str, int needed)
@ -276,13 +289,29 @@ enlargeStringInfo(StringInfo str, int needed)
* an overflow or infinite loop in the following. * an overflow or infinite loop in the following.
*/ */
if (needed < 0) /* should not happen */ if (needed < 0) /* should not happen */
{
#ifndef FRONTEND
elog(ERROR, "invalid string enlargement request size: %d", needed); elog(ERROR, "invalid string enlargement request size: %d", needed);
#else
fprintf(stderr, "invalid string enlargement request size: %d\n", needed);
exit(EXIT_FAILURE);
#endif
}
if (((Size) needed) >= (MaxAllocSize - (Size) str->len)) if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
{
#ifndef FRONTEND
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("out of memory"), errmsg("out of memory"),
errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.", errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.",
str->len, needed))); str->len, needed)));
#else
fprintf(stderr,
_("out of memory\n\nCannot enlarge string buffer containing %d bytes by %d more bytes.\n"),
str->len, needed);
exit(EXIT_FAILURE);
#endif
}
needed += str->len + 1; /* total space required now */ needed += str->len + 1; /* total space required now */

View File

@ -3,9 +3,10 @@
* stringinfo.h * stringinfo.h
* Declarations/definitions for "StringInfo" functions. * Declarations/definitions for "StringInfo" functions.
* *
* StringInfo provides an indefinitely-extensible string data type. * StringInfo provides an extensible string data type (currently limited to a
* It can be used to buffer either ordinary C strings (null-terminated text) * length of 1GB). It can be used to buffer either ordinary C strings
* or arbitrary binary data. All storage is allocated with palloc(). * (null-terminated text) or arbitrary binary data. All storage is allocated
* with palloc() (falling back to malloc in frontend code).
* *
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California

View File

@ -123,7 +123,7 @@ sub mkvcbuild
base64.c config_info.c controldata_utils.c d2s.c exec.c f2s.c file_perm.c ip.c base64.c config_info.c controldata_utils.c d2s.c exec.c f2s.c file_perm.c ip.c
keywords.c kwlookup.c link-canary.c md5.c keywords.c kwlookup.c link-canary.c md5.c
pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
saslprep.c scram-common.c string.c unicode_norm.c username.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
wait_error.c); wait_error.c);
if ($solution->{options}->{openssl}) if ($solution->{options}->{openssl})