Tighten checks for whitespace in functions that parse identifiers etc.
authorTom Lane
Wed, 24 May 2017 19:28:34 +0000 (15:28 -0400)
committerTom Lane
Wed, 24 May 2017 19:28:34 +0000 (15:28 -0400)
This patch replaces isspace() calls with scanner_isspace() in functions
that are likely to be presented with non-ASCII input.  isspace() has
the small advantage that it will correctly recognize no-break space
in single-byte encodings (such as LATIN1); but it cannot work successfully
for any multibyte character, and depending on platform it might return
false positive results for some fragments of multibyte characters.  That's
disastrous for functions that are trying to discard whitespace between
valid strings, as noted in bug #14662 from Justin Muise.  Even treating
no-break space as whitespace is pretty questionable for the usages touched
here, because the core scanner would think it is an identifier character.

Affected functions are parse_ident(), parseNameAndArgTypes (underlying
regprocedurein() and siblings), SplitIdentifierString (used for parsing
GUCs and options that are qualified names or lists of names), and
SplitDirectoriesString (used for parsing GUCs that are lists of
directories).

All the functions adjusted here are parsing SQL identifiers and similar
constructs, so it's reasonable to insist that their definition of
whitespace match the core scanner.  So we can hope that this won't cause
many backwards-compatibility problems.  I've left alone isspace() calls
in places that aren't really expecting any non-ASCII input characters,
such as float8in().

Back-patch to all supported branches.

Discussion: https://postgr.es/m/10129.1495302480@sss.pgh.pa.us

src/backend/utils/adt/misc.c
src/backend/utils/adt/regproc.c
src/backend/utils/adt/varlena.c

index 5e705e93084c05b6926e05c52a2a05e1bcdeb973..3aff7caf9e0fe2da3ea970ab46fc5a60eff63630 100644 (file)
@@ -775,7 +775,7 @@ parse_ident(PG_FUNCTION_ARGS)
    nextp = qualname_str;
 
    /* skip leading whitespace */
-   while (isspace((unsigned char) *nextp))
+   while (scanner_isspace(*nextp))
        nextp++;
 
    for (;;)
@@ -863,14 +863,14 @@ parse_ident(PG_FUNCTION_ARGS)
                                text_to_cstring(qualname))));
        }
 
-       while (isspace((unsigned char) *nextp))
+       while (scanner_isspace(*nextp))
            nextp++;
 
        if (*nextp == '.')
        {
            after_dot = true;
            nextp++;
-           while (isspace((unsigned char) *nextp))
+           while (scanner_isspace(*nextp))
                nextp++;
        }
        else if (*nextp == '\0')
index 394042cbbac4b7de8147f92734ec885a57ed05a4..210253cf42a7fcb8de0ba9c12527c5aa61729ed9 100644 (file)
@@ -35,6 +35,7 @@
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
 #include "parser/parse_type.h"
+#include "parser/scansup.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
@@ -1911,7 +1912,7 @@ parseNameAndArgTypes(const char *string, bool allowNone, List **names,
    ptr2 = ptr + strlen(ptr);
    while (--ptr2 > ptr)
    {
-       if (!isspace((unsigned char) *ptr2))
+       if (!scanner_isspace(*ptr2))
            break;
    }
    if (*ptr2 != ')')
@@ -1928,7 +1929,7 @@ parseNameAndArgTypes(const char *string, bool allowNone, List **names,
    for (;;)
    {
        /* allow leading whitespace */
-       while (isspace((unsigned char) *ptr))
+       while (scanner_isspace(*ptr))
            ptr++;
        if (*ptr == '\0')
        {
@@ -1984,7 +1985,7 @@ parseNameAndArgTypes(const char *string, bool allowNone, List **names,
        /* Lop off trailing whitespace */
        while (--ptr2 >= typename)
        {
-           if (!isspace((unsigned char) *ptr2))
+           if (!scanner_isspace(*ptr2))
                break;
            *ptr2 = '\0';
        }
index bf7c0cd73566444e092c52e6a7a46499a2d6c17d..55876bb7952513abf7955dd861a48c63480fea25 100644 (file)
@@ -3133,7 +3133,7 @@ SplitIdentifierString(char *rawstring, char separator,
 
    *namelist = NIL;
 
-   while (isspace((unsigned char) *nextp))
+   while (scanner_isspace(*nextp))
        nextp++;                /* skip leading whitespace */
 
    if (*nextp == '\0')
@@ -3171,7 +3171,7 @@ SplitIdentifierString(char *rawstring, char separator,
 
            curname = nextp;
            while (*nextp && *nextp != separator &&
-                  !isspace((unsigned char) *nextp))
+                  !scanner_isspace(*nextp))
                nextp++;
            endp = nextp;
            if (curname == nextp)
@@ -3193,13 +3193,13 @@ SplitIdentifierString(char *rawstring, char separator,
            pfree(downname);
        }
 
-       while (isspace((unsigned char) *nextp))
+       while (scanner_isspace(*nextp))
            nextp++;            /* skip trailing whitespace */
 
        if (*nextp == separator)
        {
            nextp++;
-           while (isspace((unsigned char) *nextp))
+           while (scanner_isspace(*nextp))
                nextp++;        /* skip leading whitespace for next */
            /* we expect another name, so done remains false */
        }
@@ -3258,7 +3258,7 @@ SplitDirectoriesString(char *rawstring, char separator,
 
    *namelist = NIL;
 
-   while (isspace((unsigned char) *nextp))
+   while (scanner_isspace(*nextp))
        nextp++;                /* skip leading whitespace */
 
    if (*nextp == '\0')
@@ -3295,7 +3295,7 @@ SplitDirectoriesString(char *rawstring, char separator,
            while (*nextp && *nextp != separator)
            {
                /* trailing whitespace should not be included in name */
-               if (!isspace((unsigned char) *nextp))
+               if (!scanner_isspace(*nextp))
                    endp = nextp + 1;
                nextp++;
            }
@@ -3303,13 +3303,13 @@ SplitDirectoriesString(char *rawstring, char separator,
                return false;   /* empty unquoted name not allowed */
        }
 
-       while (isspace((unsigned char) *nextp))
+       while (scanner_isspace(*nextp))
            nextp++;            /* skip trailing whitespace */
 
        if (*nextp == separator)
        {
            nextp++;
-           while (isspace((unsigned char) *nextp))
+           while (scanner_isspace(*nextp))
                nextp++;        /* skip leading whitespace for next */
            /* we expect another name, so done remains false */
        }