mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Fix write/read of empty string fields in Nodes.
Historically, outToken has represented both NULL and empty-string strings as "<>", which readfuncs.c then read as NULL, thus failing to preserve empty-string fields accurately. Remarkably, this has not caused any serious problems yet, but let's fix it. We'll keep the "<>" notation for NULL, and use """" for empty string, because that matches other notational choices already in use. An actual input string of """" is converted to "\""" (this was true already, apparently as a hangover from an ancient time when string quoting was handled directly by pg_strtok). CHAR fields also use "<>", but for '\0'. Author: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://www.postgresql.org/message-id/flat/4159834.1657405226@sss.pgh.pa.us
This commit is contained in:
@ -135,16 +135,23 @@ static void outChar(StringInfo str, char c);
|
|||||||
* Convert an ordinary string (eg, an identifier) into a form that
|
* Convert an ordinary string (eg, an identifier) into a form that
|
||||||
* will be decoded back to a plain token by read.c's functions.
|
* will be decoded back to a plain token by read.c's functions.
|
||||||
*
|
*
|
||||||
* If a null or empty string is given, it is encoded as "<>".
|
* If a null string pointer is given, it is encoded as '<>'.
|
||||||
|
* An empty string is encoded as '""'. To avoid ambiguity, input
|
||||||
|
* strings beginning with '<' or '"' receive a leading backslash.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
outToken(StringInfo str, const char *s)
|
outToken(StringInfo str, const char *s)
|
||||||
{
|
{
|
||||||
if (s == NULL || *s == '\0')
|
if (s == NULL)
|
||||||
{
|
{
|
||||||
appendStringInfoString(str, "<>");
|
appendStringInfoString(str, "<>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (*s == '\0')
|
||||||
|
{
|
||||||
|
appendStringInfoString(str, "\"\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look for characters or patterns that are treated specially by read.c
|
* Look for characters or patterns that are treated specially by read.c
|
||||||
@ -178,6 +185,13 @@ outChar(StringInfo str, char c)
|
|||||||
{
|
{
|
||||||
char in[2];
|
char in[2];
|
||||||
|
|
||||||
|
/* Traditionally, we've represented \0 as <>, so keep doing that */
|
||||||
|
if (c == '\0')
|
||||||
|
{
|
||||||
|
appendStringInfoString(str, "<>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
in[0] = c;
|
in[0] = c;
|
||||||
in[1] = '\0';
|
in[1] = '\0';
|
||||||
|
|
||||||
@ -636,7 +650,8 @@ _outString(StringInfo str, const String *node)
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We use outToken to provide escaping of the string's content, but we
|
* We use outToken to provide escaping of the string's content, but we
|
||||||
* don't want it to do anything with an empty string.
|
* don't want it to convert an empty string to '""', because we're putting
|
||||||
|
* double quotes around the string already.
|
||||||
*/
|
*/
|
||||||
appendStringInfoChar(str, '"');
|
appendStringInfoChar(str, '"');
|
||||||
if (node->sval[0] != '\0')
|
if (node->sval[0] != '\0')
|
||||||
|
@ -178,8 +178,18 @@
|
|||||||
|
|
||||||
#define strtobool(x) ((*(x) == 't') ? true : false)
|
#define strtobool(x) ((*(x) == 't') ? true : false)
|
||||||
|
|
||||||
#define nullable_string(token,length) \
|
static char *
|
||||||
((length) == 0 ? NULL : debackslash(token, length))
|
nullable_string(const char *token, int length)
|
||||||
|
{
|
||||||
|
/* outToken emits <> for NULL, and pg_strtok makes that an empty string */
|
||||||
|
if (length == 0)
|
||||||
|
return NULL;
|
||||||
|
/* outToken emits "" for empty string */
|
||||||
|
if (length == 2 && token[0] == '"' && token[1] == '"')
|
||||||
|
return pstrdup("");
|
||||||
|
/* otherwise, we must remove protective backslashes added by outToken */
|
||||||
|
return debackslash(token, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user