diff options
Diffstat (limited to 'src/bin/psql/psqlscanslash.l')
-rw-r--r-- | src/bin/psql/psqlscanslash.l | 84 |
1 files changed, 51 insertions, 33 deletions
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 |