mirror of
https://github.com/postgres/postgres.git
synced 2025-05-08 07:21:33 +03:00
Code review for psql's helpSQL() function.
The loops to identify word boundaries could access past the end of the input string. Likely that would never result in an actual crash, but it makes valgrind unhappy. The logic to try different numbers of words didn't work when the input has two words but we only have a match to the first, eg "\h with select". (We must "continue" the pass loop, not "break".) The logic to compute nl_count was bizarrely managed, and in at least two code paths could end up calling PageOutput with nl_count = 0, resulting in failing to paginate output that should have been fed to the pager. Also, in v12 and up, the nl_count calculation hadn't been updated to account for the addition of a URL. The PQExpBuffer holding the command syntax details wasn't freed, resulting in a session-lifespan memory leak. While here, improve some comments, choose a more descriptive name for a variable, fix inconsistent datatype choice for another variable. Per bug #16837 from Alexander Lakhin. This code is very old, so back-patch to all supported branches. Kyotaro Horiguchi and Tom Lane Discussion: https://postgr.es/m/16837-479bcd56040c71b3@postgresql.org
This commit is contained in:
parent
366d302d14
commit
64bdb6e5f8
@ -534,6 +534,7 @@ helpSQL(const char *topic, unsigned short int pager)
|
|||||||
int i;
|
int i;
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
|
/* Find screen width to determine how many columns will fit */
|
||||||
#ifdef TIOCGWINSZ
|
#ifdef TIOCGWINSZ
|
||||||
struct winsize screen_size;
|
struct winsize screen_size;
|
||||||
|
|
||||||
@ -571,56 +572,63 @@ helpSQL(const char *topic, unsigned short int pager)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
int i,
|
int i,
|
||||||
j,
|
pass;
|
||||||
x = 0;
|
|
||||||
bool help_found = false;
|
|
||||||
FILE *output = NULL;
|
FILE *output = NULL;
|
||||||
size_t len,
|
size_t len,
|
||||||
wordlen;
|
wordlen,
|
||||||
int nl_count = 0;
|
j;
|
||||||
|
int nl_count;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* len is the amount of the input to compare to the help topic names.
|
||||||
* We first try exact match, then first + second words, then first
|
* We first try exact match, then first + second words, then first
|
||||||
* word only.
|
* word only.
|
||||||
*/
|
*/
|
||||||
len = strlen(topic);
|
len = strlen(topic);
|
||||||
|
|
||||||
for (x = 1; x <= 3; x++)
|
for (pass = 1; pass <= 3; pass++)
|
||||||
{
|
{
|
||||||
if (x > 1) /* Nothing on first pass - try the opening
|
if (pass > 1) /* Nothing on first pass - try the opening
|
||||||
* word(s) */
|
* word(s) */
|
||||||
{
|
{
|
||||||
wordlen = j = 1;
|
wordlen = j = 1;
|
||||||
while (topic[j] != ' ' && j++ < len)
|
while (j < len && topic[j++] != ' ')
|
||||||
wordlen++;
|
wordlen++;
|
||||||
if (x == 2)
|
if (pass == 2 && j < len)
|
||||||
{
|
{
|
||||||
j++;
|
wordlen++;
|
||||||
while (topic[j] != ' ' && j++ <= len)
|
while (j < len && topic[j++] != ' ')
|
||||||
wordlen++;
|
wordlen++;
|
||||||
}
|
}
|
||||||
if (wordlen >= len) /* Don't try again if the same word */
|
if (wordlen >= len)
|
||||||
{
|
{
|
||||||
if (!output)
|
/* Failed to shorten input, so try next pass if any */
|
||||||
output = PageOutput(nl_count, pager ? &(pset.popt.topt) : NULL);
|
continue;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
len = wordlen;
|
len = wordlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Count newlines for pager */
|
/*
|
||||||
|
* Count newlines for pager. This logic must agree with what the
|
||||||
|
* following loop will do!
|
||||||
|
*/
|
||||||
|
nl_count = 0;
|
||||||
for (i = 0; QL_HELP[i].cmd; i++)
|
for (i = 0; QL_HELP[i].cmd; i++)
|
||||||
{
|
{
|
||||||
if (pg_strncasecmp(topic, QL_HELP[i].cmd, len) == 0 ||
|
if (pg_strncasecmp(topic, QL_HELP[i].cmd, len) == 0 ||
|
||||||
strcmp(topic, "*") == 0)
|
strcmp(topic, "*") == 0)
|
||||||
{
|
{
|
||||||
nl_count += 5 + QL_HELP[i].nl_count;
|
/* magic constant here must match format below! */
|
||||||
|
nl_count += 7 + QL_HELP[i].nl_count;
|
||||||
|
|
||||||
/* If we have an exact match, exit. Fixes \h SELECT */
|
/* If we have an exact match, exit. Fixes \h SELECT */
|
||||||
if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0)
|
if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* If no matches, don't open the output yet */
|
||||||
|
if (nl_count == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!output)
|
if (!output)
|
||||||
output = PageOutput(nl_count, pager ? &(pset.popt.topt) : NULL);
|
output = PageOutput(nl_count, pager ? &(pset.popt.topt) : NULL);
|
||||||
@ -635,10 +643,10 @@ helpSQL(const char *topic, unsigned short int pager)
|
|||||||
|
|
||||||
initPQExpBuffer(&buffer);
|
initPQExpBuffer(&buffer);
|
||||||
QL_HELP[i].syntaxfunc(&buffer);
|
QL_HELP[i].syntaxfunc(&buffer);
|
||||||
help_found = true;
|
|
||||||
url = psprintf("https://www.postgresql.org/docs/%s/%s.html",
|
url = psprintf("https://www.postgresql.org/docs/%s/%s.html",
|
||||||
strstr(PG_VERSION, "devel") ? "devel" : PG_MAJORVERSION,
|
strstr(PG_VERSION, "devel") ? "devel" : PG_MAJORVERSION,
|
||||||
QL_HELP[i].docbook_id);
|
QL_HELP[i].docbook_id);
|
||||||
|
/* # of newlines in format must match constant above! */
|
||||||
fprintf(output, _("Command: %s\n"
|
fprintf(output, _("Command: %s\n"
|
||||||
"Description: %s\n"
|
"Description: %s\n"
|
||||||
"Syntax:\n%s\n\n"
|
"Syntax:\n%s\n\n"
|
||||||
@ -648,17 +656,24 @@ helpSQL(const char *topic, unsigned short int pager)
|
|||||||
buffer.data,
|
buffer.data,
|
||||||
url);
|
url);
|
||||||
free(url);
|
free(url);
|
||||||
|
termPQExpBuffer(&buffer);
|
||||||
|
|
||||||
/* If we have an exact match, exit. Fixes \h SELECT */
|
/* If we have an exact match, exit. Fixes \h SELECT */
|
||||||
if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0)
|
if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (help_found) /* Don't keep trying if we got a match */
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!help_found)
|
/* If we never found anything, report that */
|
||||||
fprintf(output, _("No help available for \"%s\".\nTry \\h with no arguments to see available help.\n"), topic);
|
if (!output)
|
||||||
|
{
|
||||||
|
output = PageOutput(2, pager ? &(pset.popt.topt) : NULL);
|
||||||
|
fprintf(output, _("No help available for \"%s\".\n"
|
||||||
|
"Try \\h with no arguments to see available help.\n"),
|
||||||
|
topic);
|
||||||
|
}
|
||||||
|
|
||||||
ClosePager(output);
|
ClosePager(output);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user