Avoid code duplication in \crosstabview.
authorTom Lane <[email protected]>
Sun, 17 Apr 2016 15:37:58 +0000 (11:37 -0400)
committerTom Lane <[email protected]>
Sun, 17 Apr 2016 15:37:58 +0000 (11:37 -0400)
In commit 6f0d6a507 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.

src/bin/psql/crosstabview.c
src/bin/psql/psqlscanslash.h
src/bin/psql/psqlscanslash.l

index 71abaf3a6fe85b15f7a6507eb1b8a59bf5fcfb27..7685c6e74672f3943106cd3bf3adc158bb5e6625 100644 (file)
@@ -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;
index 48553647a905188bf9dbbf265fbeff37a436f9e7..f078f698e85c773d0314909497311739afe13745 100644 (file)
@@ -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 */
index e3e0db3b2fb50c9389f393a6912d73fe9647956a..90854afeb0e049322d589c45172e1f02b3cef938 100644 (file)
@@ -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:
@@ -667,6 +640,51 @@ psql_scan_slash_command_end(PsqlScanState 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.
  *