diff options
-rw-r--r-- | src/bin/psql/crosstabview.c | 30 | ||||
-rw-r--r-- | src/bin/psql/psqlscanslash.h | 2 | ||||
-rw-r--r-- | src/bin/psql/psqlscanslash.l | 84 |
3 files changed, 56 insertions, 60 deletions
diff --git a/src/bin/psql/crosstabview.c b/src/bin/psql/crosstabview.c index 71abaf3a6fe..7685c6e7467 100644 --- a/src/bin/psql/crosstabview.c +++ b/src/bin/psql/crosstabview.c @@ -12,6 +12,7 @@ #include "common.h" #include "crosstabview.h" #include "pqexpbuffer.h" +#include "psqlscanslash.h" #include "settings.h" @@ -648,39 +649,14 @@ indexOfColumn(char *arg, const PGresult *res) } else { - bool inquotes = false; - char *cp = arg; int i; /* * 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 - * name even if it's all digits. This transformation should match - * 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.) + * name even if it's all digits. */ - 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 (!inquotes) - *cp = pg_tolower((unsigned char) *cp); - cp += PQmblen(cp, pset.encoding); - } - } + dequote_downcase_identifier(arg, true, pset.encoding); /* Now look for match(es) among res' column names */ idx = -1; diff --git a/src/bin/psql/psqlscanslash.h b/src/bin/psql/psqlscanslash.h index 48553647a90..f078f698e85 100644 --- a/src/bin/psql/psqlscanslash.h +++ b/src/bin/psql/psqlscanslash.h @@ -32,4 +32,6 @@ extern char *psql_scan_slash_option(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 */ diff --git a/src/bin/psql/psqlscanslash.l b/src/bin/psql/psqlscanslash.l index e3e0db3b2fb..90854afeb0e 100644 --- a/src/bin/psql/psqlscanslash.l +++ b/src/bin/psql/psqlscanslash.l @@ -566,42 +566,15 @@ psql_scan_slash_option(PsqlScanState state, /* * If SQL identifier processing was requested, then we strip out - * excess double quotes and 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. + * excess double quotes and optionally downcase unquoted letters. */ if (type == OT_SQLID || type == OT_SQLIDHACK) { - bool inquotes = false; - char *cp = mybuf.data; - - 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)); - mybuf.len--; - /* do not advance cp */ - } - else - { - if (!inquotes && type == OT_SQLID) - *cp = pg_tolower((unsigned char) *cp); - cp += PQmblen(cp, state->encoding); - } - } + dequote_downcase_identifier(mybuf.data, + (type != OT_SQLIDHACK), + state->encoding); + /* update mybuf.len for possible shortening */ + mybuf.len = strlen(mybuf.data); } break; case xslashquote: @@ -668,6 +641,51 @@ psql_scan_slash_command_end(PsqlScanState 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. * * The portion of output_buf starting at backtick_start_offset is evaluated |