Commit 9603a325 authored by Tom Lane's avatar Tom Lane

Avoid code duplication in \crosstabview.

In commit 6f0d6a50 I added a duplicate copy of psqlscanslash's identifier
downcasing code, but actually it's not hard to split that out as a callable
subroutine and avoid the duplication.
parent 4039c736
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "common.h" #include "common.h"
#include "crosstabview.h" #include "crosstabview.h"
#include "pqexpbuffer.h" #include "pqexpbuffer.h"
#include "psqlscanslash.h"
#include "settings.h" #include "settings.h"
...@@ -648,39 +649,14 @@ indexOfColumn(char *arg, const PGresult *res) ...@@ -648,39 +649,14 @@ indexOfColumn(char *arg, const PGresult *res)
} }
else else
{ {
bool inquotes = false;
char *cp = arg;
int i; int i;
/* /*
* Dequote and downcase the column name. By checking for all-digits * Dequote and downcase the column name. By checking for all-digits
* before doing this, we can ensure that a quoted name is treated as a * before doing this, we can ensure that a quoted name is treated as a
* name even if it's all digits. This transformation should match * name even if it's all digits.
* what psqlscanslash.l does in OT_SQLID mode. (XXX ideally we would
* let the lexer do this, but then we couldn't tell if the name was
* quoted.)
*/ */
while (*cp) dequote_downcase_identifier(arg, true, pset.encoding);
{
if (*cp == '"')
{
if (inquotes && cp[1] == '"')
{
/* Keep the first quote, remove the second */
cp++;
}
inquotes = !inquotes;
/* Collapse out quote at *cp */
memmove(cp, cp + 1, strlen(cp));
/* do not advance cp */
}
else
{
if (!inquotes)
*cp = pg_tolower((unsigned char) *cp);
cp += PQmblen(cp, pset.encoding);
}
}
/* Now look for match(es) among res' column names */ /* Now look for match(es) among res' column names */
idx = -1; idx = -1;
......
...@@ -32,4 +32,6 @@ extern char *psql_scan_slash_option(PsqlScanState state, ...@@ -32,4 +32,6 @@ extern char *psql_scan_slash_option(PsqlScanState state,
extern void psql_scan_slash_command_end(PsqlScanState state); extern void psql_scan_slash_command_end(PsqlScanState state);
extern void dequote_downcase_identifier(char *str, bool downcase, int encoding);
#endif /* PSQLSCANSLASH_H */ #endif /* PSQLSCANSLASH_H */
...@@ -566,42 +566,15 @@ psql_scan_slash_option(PsqlScanState state, ...@@ -566,42 +566,15 @@ psql_scan_slash_option(PsqlScanState state,
/* /*
* If SQL identifier processing was requested, then we strip out * If SQL identifier processing was requested, then we strip out
* excess double quotes and downcase unquoted letters. * excess double quotes and optionally downcase unquoted letters.
* Doubled double-quotes become output double-quotes, per spec.
*
* Note that a string like FOO"BAR"BAZ will be converted to
* fooBARbaz; this is somewhat inconsistent with the SQL spec,
* which would have us parse it as several identifiers. But
* for psql's purposes, we want a string like "foo"."bar" to
* be treated as one option, so there's little choice.
*/ */
if (type == OT_SQLID || type == OT_SQLIDHACK) if (type == OT_SQLID || type == OT_SQLIDHACK)
{ {
bool inquotes = false; dequote_downcase_identifier(mybuf.data,
char *cp = mybuf.data; (type != OT_SQLIDHACK),
state->encoding);
while (*cp) /* update mybuf.len for possible shortening */
{ mybuf.len = strlen(mybuf.data);
if (*cp == '"')
{
if (inquotes && cp[1] == '"')
{
/* Keep the first quote, remove the second */
cp++;
}
inquotes = !inquotes;
/* Collapse out quote at *cp */
memmove(cp, cp + 1, strlen(cp));
mybuf.len--;
/* do not advance cp */
}
else
{
if (!inquotes && type == OT_SQLID)
*cp = pg_tolower((unsigned char) *cp);
cp += PQmblen(cp, state->encoding);
}
}
} }
break; break;
case xslashquote: case xslashquote:
...@@ -667,6 +640,51 @@ psql_scan_slash_command_end(PsqlScanState state) ...@@ -667,6 +640,51 @@ psql_scan_slash_command_end(PsqlScanState state)
psql_scan_reselect_sql_lexer(state); psql_scan_reselect_sql_lexer(state);
} }
/*
* De-quote and optionally downcase a SQL identifier.
*
* The string at *str is modified in-place; it can become shorter,
* but not longer.
*
* If downcase is true then non-quoted letters are folded to lower case.
* Ideally this behavior will match the backend's downcase_identifier();
* but note that it could differ if LC_CTYPE is different in the frontend.
*
* Note that a string like FOO"BAR"BAZ will be converted to fooBARbaz;
* this is somewhat inconsistent with the SQL spec, which would have us
* parse it as several identifiers. But for psql's purposes, we want a
* string like "foo"."bar" to be treated as one option, so there's little
* choice; this routine doesn't get to change the token boundaries.
*/
void
dequote_downcase_identifier(char *str, bool downcase, int encoding)
{
bool inquotes = false;
char *cp = str;
while (*cp)
{
if (*cp == '"')
{
if (inquotes && cp[1] == '"')
{
/* Keep the first quote, remove the second */
cp++;
}
inquotes = !inquotes;
/* Collapse out quote at *cp */
memmove(cp, cp + 1, strlen(cp));
/* do not advance cp */
}
else
{
if (downcase && !inquotes)
*cp = pg_tolower((unsigned char) *cp);
cp += PQmblen(cp, encoding);
}
}
}
/* /*
* Evaluate a backticked substring of a slash command's argument. * Evaluate a backticked substring of a slash command's argument.
* *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment