#include "common.h"
#include "crosstabview.h"
#include "pqexpbuffer.h"
+#include "psqlscanslash.h"
#include "settings.h"
}
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;
/*
* 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:
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.
*