]> granicus.if.org Git - postgresql/commitdiff
Respond to Jeremy Drake's original gripe that \copy needs to recognize
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 1 Jun 2006 01:28:00 +0000 (01:28 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 1 Jun 2006 01:28:00 +0000 (01:28 +0000)
E'...' syntax for strings in order to track the backend.

src/bin/psql/copy.c
src/bin/psql/stringutils.c
src/bin/psql/stringutils.h

index ae4ad629e999a9083a539a22c13455a8c87f09d0..ba89eb5b3892eb5255760ea7bc55da3835bfed01 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.63 2006/06/01 00:15:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.64 2006/06/01 01:28:00 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "copy.h"
@@ -127,7 +127,7 @@ parse_slash_copy(const char *args)
        result = pg_calloc(1, sizeof(struct copy_options));
 
        token = strtokx(line, whitespace, ".,()", "\"",
-                                       0, false, pset.encoding);
+                                       0, false, false, pset.encoding);
        if (!token)
                goto error;
 
@@ -135,7 +135,7 @@ parse_slash_copy(const char *args)
        {
                result->binary = true;
                token = strtokx(NULL, whitespace, ".,()", "\"",
-                                               0, false, pset.encoding);
+                                               0, false, false, pset.encoding);
                if (!token)
                        goto error;
        }
@@ -143,7 +143,7 @@ parse_slash_copy(const char *args)
        result->table = pg_strdup(token);
 
        token = strtokx(NULL, whitespace, ".,()", "\"",
-                                       0, false, pset.encoding);
+                                       0, false, false, pset.encoding);
        if (!token)
                goto error;
 
@@ -156,12 +156,12 @@ parse_slash_copy(const char *args)
                /* handle schema . table */
                xstrcat(&result->table, token);
                token = strtokx(NULL, whitespace, ".,()", "\"",
-                                               0, false, pset.encoding);
+                                               0, false, false, pset.encoding);
                if (!token)
                        goto error;
                xstrcat(&result->table, token);
                token = strtokx(NULL, whitespace, ".,()", "\"",
-                                               0, false, pset.encoding);
+                                               0, false, false, pset.encoding);
                if (!token)
                        goto error;
        }
@@ -173,12 +173,12 @@ parse_slash_copy(const char *args)
                for (;;)
                {
                        token = strtokx(NULL, whitespace, ".,()", "\"",
-                                                       0, false, pset.encoding);
+                                                       0, false, false, pset.encoding);
                        if (!token || strchr(".,()", token[0]))
                                goto error;
                        xstrcat(&result->column_list, token);
                        token = strtokx(NULL, whitespace, ".,()", "\"",
-                                                       0, false, pset.encoding);
+                                                       0, false, false, pset.encoding);
                        if (!token)
                                goto error;
                        xstrcat(&result->column_list, token);
@@ -188,7 +188,7 @@ parse_slash_copy(const char *args)
                                goto error;
                }
                token = strtokx(NULL, whitespace, ".,()", "\"",
-                                               0, false, pset.encoding);
+                                               0, false, false, pset.encoding);
                if (!token)
                        goto error;
        }
@@ -199,13 +199,13 @@ parse_slash_copy(const char *args)
        if (pg_strcasecmp(token, "with") == 0)
        {
                token = strtokx(NULL, whitespace, NULL, NULL,
-                                               0, false, pset.encoding);
+                                               0, false, false, pset.encoding);
                if (!token || pg_strcasecmp(token, "oids") != 0)
                        goto error;
                result->oids = true;
 
                token = strtokx(NULL, whitespace, NULL, NULL,
-                                               0, false, pset.encoding);
+                                               0, false, false, pset.encoding);
                if (!token)
                        goto error;
        }
@@ -218,7 +218,7 @@ parse_slash_copy(const char *args)
                goto error;
 
        token = strtokx(NULL, whitespace, NULL, "'",
-                                       nonstd_backslash, true, pset.encoding);
+                                       0, false, true, pset.encoding);
        if (!token)
                goto error;
 
@@ -242,7 +242,7 @@ parse_slash_copy(const char *args)
        }
 
        token = strtokx(NULL, whitespace, NULL, NULL,
-                                       0, false, pset.encoding);
+                                       0, false, false, pset.encoding);
 
        /*
         * Allows old COPY syntax for backward compatibility 2002-06-19
@@ -250,19 +250,19 @@ parse_slash_copy(const char *args)
        if (token && pg_strcasecmp(token, "using") == 0)
        {
                token = strtokx(NULL, whitespace, NULL, NULL,
-                                               0, false, pset.encoding);
+                                               0, false, false, pset.encoding);
                if (!(token && pg_strcasecmp(token, "delimiters") == 0))
                        goto error;
        }
        if (token && pg_strcasecmp(token, "delimiters") == 0)
        {
                token = strtokx(NULL, whitespace, NULL, "'",
-                                               nonstd_backslash, false, pset.encoding);
+                                               nonstd_backslash, true, false, pset.encoding);
                if (!token)
                        goto error;
                result->delim = pg_strdup(token);
                token = strtokx(NULL, whitespace, NULL, NULL,
-                                               0, false, pset.encoding);
+                                               0, false, false, pset.encoding);
        }
 
        if (token)
@@ -273,7 +273,7 @@ parse_slash_copy(const char *args)
                 */
                if (pg_strcasecmp(token, "with") == 0)
                        token = strtokx(NULL, whitespace, NULL, NULL,
-                                                       0, false, pset.encoding);
+                                                       0, false, false, pset.encoding);
 
                while (token)
                {
@@ -292,10 +292,10 @@ parse_slash_copy(const char *args)
                        else if (pg_strcasecmp(token, "delimiter") == 0)
                        {
                                token = strtokx(NULL, whitespace, NULL, "'",
-                                                               nonstd_backslash, false, pset.encoding);
+                                                               nonstd_backslash, true, false, pset.encoding);
                                if (token && pg_strcasecmp(token, "as") == 0)
                                        token = strtokx(NULL, whitespace, NULL, "'",
-                                                                       nonstd_backslash, false, pset.encoding);
+                                                                       nonstd_backslash, true, false, pset.encoding);
                                if (token)
                                        result->delim = pg_strdup(token);
                                else
@@ -304,10 +304,10 @@ parse_slash_copy(const char *args)
                        else if (pg_strcasecmp(token, "null") == 0)
                        {
                                token = strtokx(NULL, whitespace, NULL, "'",
-                                                               nonstd_backslash, false, pset.encoding);
+                                                               nonstd_backslash, true, false, pset.encoding);
                                if (token && pg_strcasecmp(token, "as") == 0)
                                        token = strtokx(NULL, whitespace, NULL, "'",
-                                                                       nonstd_backslash, false, pset.encoding);
+                                                                       nonstd_backslash, true, false, pset.encoding);
                                if (token)
                                        result->null = pg_strdup(token);
                                else
@@ -316,10 +316,10 @@ parse_slash_copy(const char *args)
                        else if (pg_strcasecmp(token, "quote") == 0)
                        {
                                token = strtokx(NULL, whitespace, NULL, "'",
-                                                               nonstd_backslash, false, pset.encoding);
+                                                               nonstd_backslash, true, false, pset.encoding);
                                if (token && pg_strcasecmp(token, "as") == 0)
                                        token = strtokx(NULL, whitespace, NULL, "'",
-                                                                       nonstd_backslash, false, pset.encoding);
+                                                                       nonstd_backslash, true, false, pset.encoding);
                                if (token)
                                        result->quote = pg_strdup(token);
                                else
@@ -328,10 +328,10 @@ parse_slash_copy(const char *args)
                        else if (pg_strcasecmp(token, "escape") == 0)
                        {
                                token = strtokx(NULL, whitespace, NULL, "'",
-                                                               nonstd_backslash, false, pset.encoding);
+                                                               nonstd_backslash, true, false, pset.encoding);
                                if (token && pg_strcasecmp(token, "as") == 0)
                                        token = strtokx(NULL, whitespace, NULL, "'",
-                                                                       nonstd_backslash, false, pset.encoding);
+                                                                       nonstd_backslash, true, false, pset.encoding);
                                if (token)
                                        result->escape = pg_strdup(token);
                                else
@@ -340,7 +340,7 @@ parse_slash_copy(const char *args)
                        else if (pg_strcasecmp(token, "force") == 0)
                        {
                                token = strtokx(NULL, whitespace, ",", "\"",
-                                                               0, false, pset.encoding);
+                                                               0, false, false, pset.encoding);
                                if (pg_strcasecmp(token, "quote") == 0)
                                {
                                        /* handle column list */
@@ -348,7 +348,7 @@ parse_slash_copy(const char *args)
                                        for (;;)
                                        {
                                                token = strtokx(NULL, whitespace, ",", "\"",
-                                                                               0, false, pset.encoding);
+                                                                               0, false, false, pset.encoding);
                                                if (!token || strchr(",", token[0]))
                                                        goto error;
                                                if (!result->force_quote_list)
@@ -356,7 +356,7 @@ parse_slash_copy(const char *args)
                                                else
                                                        xstrcat(&result->force_quote_list, token);
                                                token = strtokx(NULL, whitespace, ",", "\"",
-                                                                               0, false, pset.encoding);
+                                                                               0, false, false, pset.encoding);
                                                if (!token || token[0] != ',')
                                                        break;
                                                xstrcat(&result->force_quote_list, token);
@@ -365,7 +365,7 @@ parse_slash_copy(const char *args)
                                else if (pg_strcasecmp(token, "not") == 0)
                                {
                                        token = strtokx(NULL, whitespace, ",", "\"",
-                                                                       0, false, pset.encoding);
+                                                                       0, false, false, pset.encoding);
                                        if (pg_strcasecmp(token, "null") != 0)
                                                goto error;
                                        /* handle column list */
@@ -373,7 +373,7 @@ parse_slash_copy(const char *args)
                                        for (;;)
                                        {
                                                token = strtokx(NULL, whitespace, ",", "\"",
-                                                                               0, false, pset.encoding);
+                                                                               0, false, false, pset.encoding);
                                                if (!token || strchr(",", token[0]))
                                                        goto error;
                                                if (!result->force_notnull_list)
@@ -381,7 +381,7 @@ parse_slash_copy(const char *args)
                                                else
                                                        xstrcat(&result->force_notnull_list, token);
                                                token = strtokx(NULL, whitespace, ",", "\"",
-                                                                               0, false, pset.encoding);
+                                                                               0, false, false, pset.encoding);
                                                if (!token || token[0] != ',')
                                                        break;
                                                xstrcat(&result->force_notnull_list, token);
@@ -395,7 +395,7 @@ parse_slash_copy(const char *args)
 
                        if (fetch_next)
                                token = strtokx(NULL, whitespace, NULL, NULL,
-                                                               0, false, pset.encoding);
+                                                               0, false, false, pset.encoding);
                }
        }
 
@@ -415,6 +415,22 @@ error:
 }
 
 
+/*
+ * Handle one of the "string" options of COPY.  If the user gave a quoted
+ * string, pass it to the backend as-is; if it wasn't quoted then quote
+ * and escape it.
+ */
+static void
+emit_copy_option(PQExpBuffer query, const char *keyword, const char *option)
+{
+       appendPQExpBufferStr(query, keyword);
+       if (option[0] == '\'' ||
+               ((option[0] == 'E' || option[0] == 'e') && option[1] == '\''))
+               appendPQExpBufferStr(query, option);
+       else
+               appendStringLiteralConn(query, option, pset.db);
+}
+
 
 /*
  * Execute a \copy command (frontend copy). We have to open a file, then
@@ -462,29 +478,11 @@ do_copy(const char *args)
 
        /* Uses old COPY syntax for backward compatibility 2002-06-19 */
        if (options->delim)
-       {
-               /* if user gave a quoted string, use it as-is */
-               if (options->delim[0] == '\'')
-                       appendPQExpBuffer(&query, " USING DELIMITERS %s", options->delim);
-               else
-               {
-                       appendPQExpBuffer(&query, " USING DELIMITERS ");
-                       appendStringLiteralConn(&query, options->delim, pset.db);
-               }
-       }
+               emit_copy_option(&query, " USING DELIMITERS ", options->delim);
 
        /* There is no backward-compatible CSV syntax */
        if (options->null)
-       {
-               /* if user gave a quoted string, use it as-is */
-               if (options->null[0] == '\'')
-                       appendPQExpBuffer(&query, " WITH NULL AS %s", options->null);
-               else
-               {
-                       appendPQExpBuffer(&query, " WITH NULL AS ");
-                       appendStringLiteralConn(&query, options->null, pset.db);
-               }
-       }
+               emit_copy_option(&query, " WITH NULL AS ", options->null);
 
        if (options->csv_mode)
                appendPQExpBuffer(&query, " CSV");
@@ -493,28 +491,10 @@ do_copy(const char *args)
                appendPQExpBuffer(&query, " HEADER");
 
        if (options->quote)
-       {
-               /* if user gave a quoted string, use it as-is */
-               if (options->quote[0] == '\'')
-                       appendPQExpBuffer(&query, " QUOTE AS %s", options->quote);
-               else
-               {
-                       appendPQExpBuffer(&query, " QUOTE AS ");
-                       appendStringLiteralConn(&query, options->quote, pset.db);
-               }
-       }
+               emit_copy_option(&query, " QUOTE AS ", options->quote);
 
        if (options->escape)
-       {
-               /* if user gave a quoted string, use it as-is */
-               if (options->escape[0] == '\'')
-                       appendPQExpBuffer(&query, " ESCAPE AS %s", options->escape);
-               else
-               {
-                       appendPQExpBuffer(&query, " ESCAPE AS ");
-                       appendStringLiteralConn(&query, options->escape, pset.db);
-               }
-       }
+               emit_copy_option(&query, " ESCAPE AS ", options->escape);
 
        if (options->force_quote_list)
                appendPQExpBuffer(&query, " FORCE QUOTE %s", options->force_quote_list);
index 4662596edd4c56fefb480ace33e97d99290fb30c..e3bb71d7f9784b1d0c4653a1b35808a2b0a09096 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/stringutils.c,v 1.42 2006/03/05 15:58:52 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/stringutils.c,v 1.43 2006/06/01 01:28:00 tgl Exp $
  */
 #include "postgres_fe.h"
 
@@ -32,7 +32,8 @@ static void strip_quotes(char *source, char quote, char escape, int encoding);
  * delim -             set of non-whitespace separator characters (or NULL)
  * quote -             set of characters that can quote a token (NULL if none)
  * escape -            character that can quote quotes (0 if none)
- * del_quotes - if TRUE, strip quotes from the returned token, else return
+ * e_strings - if TRUE, treat E'...' syntax as a valid token
+ * del_quotes -        if TRUE, strip quotes from the returned token, else return
  *                             it exactly as found in the string
  * encoding -  the active character-set encoding
  *
@@ -43,6 +44,9 @@ static void strip_quotes(char *source, char quote, char escape, int encoding);
  * a single quote character in the data.  If escape isn't 0, then escape
  * followed by anything (except \0) is a data character too.
  *
+ * The combination of e_strings and del_quotes both TRUE is not currently
+ * handled.  This could be fixed but it's not needed anywhere at the moment.
+ *
  * Note that the string s is _not_ overwritten in this implementation.
  *
  * NB: it's okay to vary delim, quote, and escape from one call to the
@@ -55,6 +59,7 @@ strtokx(const char *s,
                const char *delim,
                const char *quote,
                char escape,
+               bool e_strings,
                bool del_quotes,
                int encoding)
 {
@@ -126,13 +131,24 @@ strtokx(const char *s,
                return start;
        }
 
+       /* check for E string */
+       p = start;
+       if (e_strings &&
+               (*p == 'E' || *p == 'e') &&
+               p[1] == '\'')
+       {
+               quote = "'";
+               escape = '\\';                  /* if std strings before, not any more */
+               p++;
+       }
+
        /* test if quoting character */
-       if (quote && strchr(quote, *start))
+       if (quote && strchr(quote, *p))
        {
                /* okay, we have a quoted token, now scan for the closer */
-               char            thisquote = *start;
+               char            thisquote = *p++;
 
-               for (p = start + 1; *p; p += PQmblen(p, encoding))
+               for (; *p; p += PQmblen(p, encoding))
                {
                        if (*p == escape && p[1] != '\0')
                                p++;                    /* process escaped anything */
index 1cf50f2b387a07646227440a9222e4bc10a3d57f..499eb631937c6604bc23bdb0de940f83af343ba3 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/stringutils.h,v 1.23 2006/03/05 15:58:52 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/stringutils.h,v 1.24 2006/06/01 01:28:00 tgl Exp $
  */
 #ifndef STRINGUTILS_H
 #define STRINGUTILS_H
@@ -15,6 +15,7 @@ extern char *strtokx(const char *s,
                const char *delim,
                const char *quote,
                char escape,
+               bool e_strings,
                bool del_quotes,
                int encoding);