Thread: Re: [HACKERS] psql \copy warning

Re: [HACKERS] psql \copy warning

From
Bruce Momjian
Date:
Jeremy Drake wrote:
> I use the \copy command from psql to load data into postgres.  I was
> fiddling with setting up a database on a HEAD build, and I got the
> following new warning
>
> testy=# \copy episodes from 'episodes.data' with delimiter as '\t'
> WARNING:  nonstandard use of escape in a string literal
> LINE 1: COPY episodes FROM STDIN USING DELIMITERS '\t'
>                                                   ^
> HINT:  Use the escape string syntax for escapes, e.g., E'\r\n'.
>
>
> I figured that this is the new standards conforming strings feature, and I
> guess I should get used to the new escape syntax.  So I tried the hint
>
> testy=# \copy episodes FROM 'episodes.data' with delimiter as E'\t'
> \copy: parse error at "'\t'"
>
> So is it just me, or is this decidedly non-helpful?  I assume someone
> missed this place for the new syntax tweaks?

Interesting bug report.  The basic question is whether \copy should
follow the quoting rules of the SQL server, or of psql.  Most psql
arguments have backslashes enabled, so I am thinking \copy should as
well, and not match COPY if standard_compliant_strings is on.

The attached patch fixes the warning you received by adding E'' strings
to the \copy arguments, and adds it for the other backslash commands
like \d.

I don't think we want a psql escape control setting, nor do we want to
remove backslash controls from psql commands (they are not SQL
standard).

--
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: src/bin/psql/command.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.166
diff -c -c -r1.166 command.c
*** src/bin/psql/command.c    2 Apr 2006 20:08:22 -0000    1.166
--- src/bin/psql/command.c    28 May 2006 03:22:10 -0000
***************
*** 681,688 ****
                  PGresult   *res;

                  initPQExpBuffer(&buf);
!                 printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD '%s';",
!                                   fmtId(user), encrypted_password);
                  res = PSQLexec(buf.data, false);
                  termPQExpBuffer(&buf);
                  if (!res)
--- 681,689 ----
                  PGresult   *res;

                  initPQExpBuffer(&buf);
!                 printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD %c'%s';",
!                                   fmtId(user), NEED_E_STR(encrypted_password),
!                                   encrypted_password);
                  res = PSQLexec(buf.data, false);
                  termPQExpBuffer(&buf);
                  if (!res)
Index: src/bin/psql/common.h
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/common.h,v
retrieving revision 1.47
diff -c -c -r1.47 common.h
*** src/bin/psql/common.h    6 Mar 2006 19:49:20 -0000    1.47
--- src/bin/psql/common.h    28 May 2006 03:22:10 -0000
***************
*** 22,27 ****
--- 22,29 ----

  #define atooid(x)  ((Oid) strtoul((x), NULL, 10))

+ #define    NEED_E_STR(str)        (strchr(str, '\\') ? ESCAPE_STRING_SYNTAX : ' ')
+
  /*
   * Safer versions of some standard C library functions. If an
   * out-of-memory condition occurs, these functions will bail out
Index: src/bin/psql/copy.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/copy.c,v
retrieving revision 1.61
diff -c -c -r1.61 copy.c
*** src/bin/psql/copy.c    26 May 2006 19:51:29 -0000    1.61
--- src/bin/psql/copy.c    28 May 2006 03:22:10 -0000
***************
*** 462,481 ****
      if (options->delim)
      {
          if (options->delim[0] == '\'')
!             appendPQExpBuffer(&query, " USING DELIMITERS %s",
!                               options->delim);
          else
!             appendPQExpBuffer(&query, " USING DELIMITERS '%s'",
!                               options->delim);
      }

      /* There is no backward-compatible CSV syntax */
      if (options->null)
      {
          if (options->null[0] == '\'')
!             appendPQExpBuffer(&query, " WITH NULL AS %s", options->null);
          else
!             appendPQExpBuffer(&query, " WITH NULL AS '%s'", options->null);
      }

      if (options->csv_mode)
--- 462,483 ----
      if (options->delim)
      {
          if (options->delim[0] == '\'')
!             appendPQExpBuffer(&query, " USING DELIMITERS %c%s",
!                               NEED_E_STR(options->delim), options->delim);
          else
!             appendPQExpBuffer(&query, " USING DELIMITERS %c'%s'",
!                               NEED_E_STR(options->delim), options->delim);
      }

      /* There is no backward-compatible CSV syntax */
      if (options->null)
      {
          if (options->null[0] == '\'')
!             appendPQExpBuffer(&query, " WITH NULL AS %c%s",
!                               NEED_E_STR(options->null), options->null);
          else
!             appendPQExpBuffer(&query, " WITH NULL AS %c'%s'",
!                               NEED_E_STR(options->null), options->null);
      }

      if (options->csv_mode)
***************
*** 487,503 ****
      if (options->quote)
      {
          if (options->quote[0] == '\'')
!             appendPQExpBuffer(&query, " QUOTE AS %s", options->quote);
          else
!             appendPQExpBuffer(&query, " QUOTE AS '%s'", options->quote);
      }

      if (options->escape)
      {
          if (options->escape[0] == '\'')
!             appendPQExpBuffer(&query, " ESCAPE AS %s", options->escape);
          else
!             appendPQExpBuffer(&query, " ESCAPE AS '%s'", options->escape);
      }

      if (options->force_quote_list)
--- 489,509 ----
      if (options->quote)
      {
          if (options->quote[0] == '\'')
!             appendPQExpBuffer(&query, " QUOTE AS %c%s",
!                               NEED_E_STR(options->quote), options->quote);
          else
!             appendPQExpBuffer(&query, " QUOTE AS %c'%s'",
!                               NEED_E_STR(options->quote), options->quote);
      }

      if (options->escape)
      {
          if (options->escape[0] == '\'')
!             appendPQExpBuffer(&query, " ESCAPE AS %c%s",
!                               NEED_E_STR(options->escape), options->escape);
          else
!             appendPQExpBuffer(&query, " ESCAPE AS %c'%s'",
!                               NEED_E_STR(options->escape), options->escape);
      }

      if (options->force_quote_list)
Index: src/bin/psql/describe.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/describe.c,v
retrieving revision 1.136
diff -c -c -r1.136 describe.c
*** src/bin/psql/describe.c    28 May 2006 02:27:08 -0000    1.136
--- src/bin/psql/describe.c    28 May 2006 03:22:11 -0000
***************
*** 1902,1915 ****
              WHEREAND();
              if (altnamevar)
                  appendPQExpBuffer(buf,
!                                   "(%s ~ '^%s'\n"
!                                   "        OR %s ~ '^%s')\n",
!                                   namevar, namebuf.data,
!                                   altnamevar, namebuf.data);
              else
                  appendPQExpBuffer(buf,
!                                   "%s ~ '^%s'\n",
!                                   namevar, namebuf.data);
          }
      }

--- 1902,1917 ----
              WHEREAND();
              if (altnamevar)
                  appendPQExpBuffer(buf,
!                                   "(%s ~ %c'^%s'\n"
!                                   "        OR %s ~ %c'^%s')\n",
!                                   namevar, NEED_E_STR(namebuf.data),
!                                   namebuf.data, altnamevar,
!                                   NEED_E_STR(namebuf.data), namebuf.data);
              else
                  appendPQExpBuffer(buf,
!                                   "%s ~ %c'^%s'\n",
!                                   namevar, NEED_E_STR(namebuf.data),
!                                   namebuf.data);
          }
      }

***************
*** 1926,1933 ****
          if (schemabuf.data[0] && schemavar)
          {
              WHEREAND();
!             appendPQExpBuffer(buf, "%s ~ '^%s'\n",
!                               schemavar, schemabuf.data);
          }
      }
      else
--- 1928,1936 ----
          if (schemabuf.data[0] && schemavar)
          {
              WHEREAND();
!             appendPQExpBuffer(buf, "%s ~ %c'^%s'\n",
!                               schemavar, NEED_E_STR(schemabuf.data),
!                               schemabuf.data);
          }
      }
      else

Re: [HACKERS] psql \copy warning

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> The attached patch fixes the warning you received by adding E'' strings
> to the \copy arguments, and adds it for the other backslash commands
> like \d.

You missed the point entirely Bruce.  The problem is that \copy's
argument parsing won't accept an option specified as E'\t' --- I believe
it is seeing that as two arguments instead of one.

The patch you propose addresses a completely different issue, which is
whether we are going to E-ify all our utilities so they don't trigger
the escape_string_warning patch.  I don't think that that is the right
direction to go in.  In fact, based on what I was doing this afternoon,
my feeling is that 8.2 will not ship with escape_string_warning turned
on by default.  It's a good tool for testing code when you're trying to
move the code over to standard conforming strings, but it's just too
noisy for code that in point of fact is already fixed.

            regards, tom lane

Re: [HACKERS] psql \copy warning

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> The attached patch fixes the warning you received by adding E'' strings
> to the \copy arguments, and adds it for the other backslash commands
> like \d.

Further comment on this: I don't think we want all these places
individually making this sort of decision.  What they should all be
doing is using appendStringLiteralConn to generate the properly-quoted
literal.  (I fixed this already in describe.c, but not in those other
places.)

Once we've got that done, we could argue about whether appendStringLiteral
ought to prepend an E to silence escape_string_warning.  I'd still vote
no, but at least it would be a single place to change and not N of 'em.
What's more, each place that is generating a variable literal without
using appendStringLiteral or PQescapeString is at least potentially
vulnerable to encoding issues, and so we should probably convert them
anyway.

            regards, tom lane

Re: [HACKERS] psql \copy warning

From
Bruce Momjian
Date:
Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > The attached patch fixes the warning you received by adding E'' strings
> > to the \copy arguments, and adds it for the other backslash commands
> > like \d.
>
> You missed the point entirely Bruce.  The problem is that \copy's
> argument parsing won't accept an option specified as E'\t' --- I believe
> it is seeing that as two arguments instead of one.

Right.  I think the question is whether we want all psql strings to
accept backslashes, and hence not support E'' at all for psql commands.
I figured that made the most sense.

> The patch you propose addresses a completely different issue, which is
> whether we are going to E-ify all our utilities so they don't trigger
> the escape_string_warning patch.  I don't think that that is the right
> direction to go in.  In fact, based on what I was doing this afternoon,
> my feeling is that 8.2 will not ship with escape_string_warning turned
> on by default.  It's a good tool for testing code when you're trying to
> move the code over to standard conforming strings, but it's just too
> noisy for code that in point of fact is already fixed.

Well, we can remove the change I made to ruleutils.c::get_const_expr()
because you are right, we are setting standard_conforming_strings in
pg_dump, so we know the value and E'' is not necessary.  We might also
want to turn off escape_string_warning in pg_dump as well.

As far as psql, we could check standard_conforming_strings and just use
the proper escaping without using E'', assuming escape_string_warning
is off.

escape_string_warning was really designed as a portability tool, so if
we don't want to use it for our utilities, that is fine, as long as we
are checking standard_conforming_strings.

If we don't set escape_string_warning on for a release, will we be able
to turn on standard_conforming_strings?  I just don't know.

--
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: [HACKERS] psql \copy warning

From
Bruce Momjian
Date:
Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > The attached patch fixes the warning you received by adding E'' strings
> > to the \copy arguments, and adds it for the other backslash commands
> > like \d.
>
> Further comment on this: I don't think we want all these places
> individually making this sort of decision.  What they should all be
> doing is using appendStringLiteralConn to generate the properly-quoted
> literal.  (I fixed this already in describe.c, but not in those other
> places.)

Yes, I think so.

> Once we've got that done, we could argue about whether appendStringLiteral
> ought to prepend an E to silence escape_string_warning.  I'd still vote
> no, but at least it would be a single place to change and not N of 'em.
> What's more, each place that is generating a variable literal without
> using appendStringLiteral or PQescapeString is at least potentially
> vulnerable to encoding issues, and so we should probably convert them
> anyway.

True.  See the email I just sent about escape_string_warning.  FYI, we
are finding these places because of escape_string_warning.  Do we trust
users to turn that on and test before standard_conforming_strings
becomes true.  One big problem is that people are having to use E'' if
they want to keep using backslashes, even if they have already tested
standard_conforming_strings.  One nice thing about E'' is that it works
no matter what the value of standard_conforming_strings is.

--
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: [HACKERS] psql \copy warning

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> Right.  I think the question is whether we want all psql strings to
> accept backslashes, and hence not support E'' at all for psql commands.
> I figured that made the most sense.

I'm not convinced.  Wouldn't it be better if psql commands track the
backend syntax?  With standard_conforming_strings on, there will be two
ways to tell COPY you want a tab as a delimiter:
    DELIMITER '<actual tab char>'
    DELIMITER E'\t'
and in particular this will NOT do that:
    DELIMITER '\t'

If we keep '\t' as meaning tab in the \copy syntax then I think we're
going to cause confusion in the long run.  I think we should fix \copy
and related psql backslash commands to accept E'\t', and make sure that
the behavior is the same as the connected backend depending on what its
standard_conforming_strings setting is.

There is a secondary, largely cosmetic question of whether psql should
attempt to prevent you from seeing escape_string_warning messages.
I personally have come to the conclusion that escape_string_warning is
probably not going to be on by default anyway ;-), and hence it's not
worth going to great extremes to prevent this, particularly if it breaks
the ability to use psql against pre-8.1 servers.

            regards, tom lane

Re: [HACKERS] psql \copy warning

From
Bruce Momjian
Date:
Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > Right.  I think the question is whether we want all psql strings to
> > accept backslashes, and hence not support E'' at all for psql commands.
> > I figured that made the most sense.
>
> I'm not convinced.  Wouldn't it be better if psql commands track the
> backend syntax?  With standard_conforming_strings on, there will be two
> ways to tell COPY you want a tab as a delimiter:
>     DELIMITER '<actual tab char>'
>     DELIMITER E'\t'
> and in particular this will NOT do that:
>     DELIMITER '\t'

Well, I think it a little more confusing that just \copy.  What about \d
and \set uses of backslashes.  Do they honor standard_conforming_strings
too?  I assume you are saying they should.

> If we keep '\t' as meaning tab in the \copy syntax then I think we're
> going to cause confusion in the long run.  I think we should fix \copy
> and related psql backslash commands to accept E'\t', and make sure that
> the behavior is the same as the connected backend depending on what its
> standard_conforming_strings setting is.

OK, though this is going to mean that examples in the psql manual page
are going to be different for different standard_conforming_strings
settings:

       testdb=> \set content '\'' `cat my_file.txt` '\''
       testdb=> INSERT INTO my_table VALUES (:content);

psql doesn't know '''' is about doubling single quotes in a string,
though \copy does.  The major problem, I think, is that psql often
follows the shell rules, rather than the SQL rules for most things.

> There is a secondary, largely cosmetic question of whether psql should
> attempt to prevent you from seeing escape_string_warning messages.
> I personally have come to the conclusion that escape_string_warning is
> probably not going to be on by default anyway ;-), and hence it's not
> worth going to great extremes to prevent this, particularly if it breaks
> the ability to use psql against pre-8.1 servers.

It does break backward compatibility.

--
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: [HACKERS] psql \copy warning

From
Bruce Momjian
Date:
I have developed an updated patch that:

    o  turns off escape_string_warning in pg_dumpall.c
    o  optionally use E'' for \password (undocumented option?)
    o  honor standard_conforming-strings for \copy (but not
       support literal E'' strings)
    o  optionally use E'' for \d commands
    o  turn off escape_string_warning for createdb, createuser,
       droplang

I agree someday we might want to turn off escape_string_warning, but I
think we should leave it on as long as possible as it is still pointing
out escape problem areas in the code.

---------------------------------------------------------------------------

Bruce Momjian wrote:
> Tom Lane wrote:
> > Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > > Right.  I think the question is whether we want all psql strings to
> > > accept backslashes, and hence not support E'' at all for psql commands.
> > > I figured that made the most sense.
> >
> > I'm not convinced.  Wouldn't it be better if psql commands track the
> > backend syntax?  With standard_conforming_strings on, there will be two
> > ways to tell COPY you want a tab as a delimiter:
> >     DELIMITER '<actual tab char>'
> >     DELIMITER E'\t'
> > and in particular this will NOT do that:
> >     DELIMITER '\t'
>
> Well, I think it a little more confusing that just \copy.  What about \d
> and \set uses of backslashes.  Do they honor standard_conforming_strings
> too?  I assume you are saying they should.
>
> > If we keep '\t' as meaning tab in the \copy syntax then I think we're
> > going to cause confusion in the long run.  I think we should fix \copy
> > and related psql backslash commands to accept E'\t', and make sure that
> > the behavior is the same as the connected backend depending on what its
> > standard_conforming_strings setting is.
>
> OK, though this is going to mean that examples in the psql manual page
> are going to be different for different standard_conforming_strings
> settings:
>
>        testdb=> \set content '\'' `cat my_file.txt` '\''
>        testdb=> INSERT INTO my_table VALUES (:content);
>
> psql doesn't know '''' is about doubling single quotes in a string,
> though \copy does.  The major problem, I think, is that psql often
> follows the shell rules, rather than the SQL rules for most things.
>
> > There is a secondary, largely cosmetic question of whether psql should
> > attempt to prevent you from seeing escape_string_warning messages.
> > I personally have come to the conclusion that escape_string_warning is
> > probably not going to be on by default anyway ;-), and hence it's not
> > worth going to great extremes to prevent this, particularly if it breaks
> > the ability to use psql against pre-8.1 servers.
>
> It does break backward compatibility.
>
> --
>   Bruce Momjian   http://candle.pha.pa.us
>   EnterpriseDB    http://www.enterprisedb.com
>
>   + If your life is a hard drive, Christ can be your backup. +
>
> ---------------------------(end of broadcast)---------------------------
> TIP 4: Have you searched our list archives?
>
>                http://archives.postgresql.org
>

--
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: src/bin/pg_dump/pg_dumpall.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/pg_dump/pg_dumpall.c,v
retrieving revision 1.77
diff -c -c -r1.77 pg_dumpall.c
*** src/bin/pg_dump/pg_dumpall.c    28 May 2006 21:13:54 -0000    1.77
--- src/bin/pg_dump/pg_dumpall.c    29 May 2006 20:41:01 -0000
***************
*** 338,343 ****
--- 338,345 ----
          printf("SET client_encoding = '%s';\n",
                 pg_encoding_to_char(encoding));
          printf("SET standard_conforming_strings = %s;\n", std_strings);
+         if (strcmp(std_strings, "off") == 0)
+             printf("SET escape_string_warning = 'off';\n");
          printf("\n");

          /* Dump roles (users) */
Index: src/bin/psql/command.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.166
diff -c -c -r1.166 command.c
*** src/bin/psql/command.c    2 Apr 2006 20:08:22 -0000    1.166
--- src/bin/psql/command.c    29 May 2006 20:41:02 -0000
***************
*** 681,688 ****
                  PGresult   *res;

                  initPQExpBuffer(&buf);
!                 printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD '%s';",
!                                   fmtId(user), encrypted_password);
                  res = PSQLexec(buf.data, false);
                  termPQExpBuffer(&buf);
                  if (!res)
--- 681,689 ----
                  PGresult   *res;

                  initPQExpBuffer(&buf);
!                 printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD %c'%s';",
!                                   fmtId(user), NEED_E_STR(encrypted_password),
!                                   encrypted_password);
                  res = PSQLexec(buf.data, false);
                  termPQExpBuffer(&buf);
                  if (!res)
Index: src/bin/psql/common.h
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/common.h,v
retrieving revision 1.47
diff -c -c -r1.47 common.h
*** src/bin/psql/common.h    6 Mar 2006 19:49:20 -0000    1.47
--- src/bin/psql/common.h    29 May 2006 20:41:03 -0000
***************
*** 23,28 ****
--- 23,34 ----
  #define atooid(x)  ((Oid) strtoul((x), NULL, 10))

  /*
+  *    We use this to prefix strings with E'' that we know are already safe,
+  *    so we don't get an escape_string_warning.
+  */
+ #define    NEED_E_STR(str)        ((strchr(str, '\\') && !standard_strings()) ? ESCAPE_STRING_SYNTAX : ' ')
+
+ /*
   * Safer versions of some standard C library functions. If an
   * out-of-memory condition occurs, these functions will bail out
   * safely; therefore, their return value is guaranteed to be non-NULL.
Index: src/bin/psql/copy.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/copy.c,v
retrieving revision 1.61
diff -c -c -r1.61 copy.c
*** src/bin/psql/copy.c    26 May 2006 19:51:29 -0000    1.61
--- src/bin/psql/copy.c    29 May 2006 20:41:03 -0000
***************
*** 216,222 ****
          goto error;

      token = strtokx(NULL, whitespace, NULL, "'",
!                     '\\', true, pset.encoding);
      if (!token)
          goto error;

--- 216,222 ----
          goto error;

      token = strtokx(NULL, whitespace, NULL, "'",
!                     standard_strings() ? 0 : '\\', true, pset.encoding);
      if (!token)
          goto error;

***************
*** 255,261 ****
      if (token && pg_strcasecmp(token, "delimiters") == 0)
      {
          token = strtokx(NULL, whitespace, NULL, "'",
!                         '\\', false, pset.encoding);
          if (!token)
              goto error;
          result->delim = pg_strdup(token);
--- 255,261 ----
      if (token && pg_strcasecmp(token, "delimiters") == 0)
      {
          token = strtokx(NULL, whitespace, NULL, "'",
!                         standard_strings() ? 0 : '\\', false, pset.encoding);
          if (!token)
              goto error;
          result->delim = pg_strdup(token);
***************
*** 290,299 ****
              else if (pg_strcasecmp(token, "delimiter") == 0)
              {
                  token = strtokx(NULL, whitespace, NULL, "'",
!                                 '\\', false, pset.encoding);
                  if (token && pg_strcasecmp(token, "as") == 0)
                      token = strtokx(NULL, whitespace, NULL, "'",
!                                     '\\', false, pset.encoding);
                  if (token)
                      result->delim = pg_strdup(token);
                  else
--- 290,299 ----
              else if (pg_strcasecmp(token, "delimiter") == 0)
              {
                  token = strtokx(NULL, whitespace, NULL, "'",
!                                 standard_strings() ? 0 : '\\', false, pset.encoding);
                  if (token && pg_strcasecmp(token, "as") == 0)
                      token = strtokx(NULL, whitespace, NULL, "'",
!                                     standard_strings() ? 0 : '\\', false, pset.encoding);
                  if (token)
                      result->delim = pg_strdup(token);
                  else
***************
*** 302,311 ****
              else if (pg_strcasecmp(token, "null") == 0)
              {
                  token = strtokx(NULL, whitespace, NULL, "'",
!                                 '\\', false, pset.encoding);
                  if (token && pg_strcasecmp(token, "as") == 0)
                      token = strtokx(NULL, whitespace, NULL, "'",
!                                     '\\', false, pset.encoding);
                  if (token)
                      result->null = pg_strdup(token);
                  else
--- 302,311 ----
              else if (pg_strcasecmp(token, "null") == 0)
              {
                  token = strtokx(NULL, whitespace, NULL, "'",
!                                 standard_strings() ? 0 : '\\', false, pset.encoding);
                  if (token && pg_strcasecmp(token, "as") == 0)
                      token = strtokx(NULL, whitespace, NULL, "'",
!                                     standard_strings() ? 0 : '\\', false, pset.encoding);
                  if (token)
                      result->null = pg_strdup(token);
                  else
***************
*** 314,323 ****
              else if (pg_strcasecmp(token, "quote") == 0)
              {
                  token = strtokx(NULL, whitespace, NULL, "'",
!                                 '\\', false, pset.encoding);
                  if (token && pg_strcasecmp(token, "as") == 0)
                      token = strtokx(NULL, whitespace, NULL, "'",
!                                     '\\', false, pset.encoding);
                  if (token)
                      result->quote = pg_strdup(token);
                  else
--- 314,323 ----
              else if (pg_strcasecmp(token, "quote") == 0)
              {
                  token = strtokx(NULL, whitespace, NULL, "'",
!                                 standard_strings() ? 0 : '\\', false, pset.encoding);
                  if (token && pg_strcasecmp(token, "as") == 0)
                      token = strtokx(NULL, whitespace, NULL, "'",
!                                     standard_strings() ? 0 : '\\', false, pset.encoding);
                  if (token)
                      result->quote = pg_strdup(token);
                  else
***************
*** 326,335 ****
              else if (pg_strcasecmp(token, "escape") == 0)
              {
                  token = strtokx(NULL, whitespace, NULL, "'",
!                                 '\\', false, pset.encoding);
                  if (token && pg_strcasecmp(token, "as") == 0)
                      token = strtokx(NULL, whitespace, NULL, "'",
!                                     '\\', false, pset.encoding);
                  if (token)
                      result->escape = pg_strdup(token);
                  else
--- 326,335 ----
              else if (pg_strcasecmp(token, "escape") == 0)
              {
                  token = strtokx(NULL, whitespace, NULL, "'",
!                                 standard_strings() ? 0 : '\\', false, pset.encoding);
                  if (token && pg_strcasecmp(token, "as") == 0)
                      token = strtokx(NULL, whitespace, NULL, "'",
!                                     standard_strings() ? 0 : '\\', false, pset.encoding);
                  if (token)
                      result->escape = pg_strdup(token);
                  else
***************
*** 462,481 ****
      if (options->delim)
      {
          if (options->delim[0] == '\'')
!             appendPQExpBuffer(&query, " USING DELIMITERS %s",
!                               options->delim);
          else
!             appendPQExpBuffer(&query, " USING DELIMITERS '%s'",
!                               options->delim);
      }

      /* There is no backward-compatible CSV syntax */
      if (options->null)
      {
          if (options->null[0] == '\'')
!             appendPQExpBuffer(&query, " WITH NULL AS %s", options->null);
          else
!             appendPQExpBuffer(&query, " WITH NULL AS '%s'", options->null);
      }

      if (options->csv_mode)
--- 462,483 ----
      if (options->delim)
      {
          if (options->delim[0] == '\'')
!             appendPQExpBuffer(&query, " USING DELIMITERS %c%s",
!                               NEED_E_STR(options->delim), options->delim);
          else
!             appendPQExpBuffer(&query, " USING DELIMITERS %c'%s'",
!                               NEED_E_STR(options->delim), options->delim);
      }

      /* There is no backward-compatible CSV syntax */
      if (options->null)
      {
          if (options->null[0] == '\'')
!             appendPQExpBuffer(&query, " WITH NULL AS %c%s",
!                               NEED_E_STR(options->null), options->null);
          else
!             appendPQExpBuffer(&query, " WITH NULL AS %c'%s'",
!                               NEED_E_STR(options->null), options->null);
      }

      if (options->csv_mode)
***************
*** 487,503 ****
      if (options->quote)
      {
          if (options->quote[0] == '\'')
!             appendPQExpBuffer(&query, " QUOTE AS %s", options->quote);
          else
!             appendPQExpBuffer(&query, " QUOTE AS '%s'", options->quote);
      }

      if (options->escape)
      {
          if (options->escape[0] == '\'')
!             appendPQExpBuffer(&query, " ESCAPE AS %s", options->escape);
          else
!             appendPQExpBuffer(&query, " ESCAPE AS '%s'", options->escape);
      }

      if (options->force_quote_list)
--- 489,509 ----
      if (options->quote)
      {
          if (options->quote[0] == '\'')
!             appendPQExpBuffer(&query, " QUOTE AS %c%s",
!                               NEED_E_STR(options->quote), options->quote);
          else
!             appendPQExpBuffer(&query, " QUOTE AS %c'%s'",
!                               NEED_E_STR(options->quote), options->quote);
      }

      if (options->escape)
      {
          if (options->escape[0] == '\'')
!             appendPQExpBuffer(&query, " ESCAPE AS %c%s",
!                               NEED_E_STR(options->escape), options->escape);
          else
!             appendPQExpBuffer(&query, " ESCAPE AS %c'%s'",
!                               NEED_E_STR(options->escape), options->escape);
      }

      if (options->force_quote_list)
Index: src/bin/psql/describe.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/describe.c,v
retrieving revision 1.137
diff -c -c -r1.137 describe.c
*** src/bin/psql/describe.c    28 May 2006 21:13:54 -0000    1.137
--- src/bin/psql/describe.c    29 May 2006 20:41:04 -0000
***************
*** 1907,1920 ****
--- 1907,1923 ----
              if (altnamevar)
              {
                  appendPQExpBuffer(buf, "(%s ~ ", namevar);
+                 appendPQExpBufferChar(buf, NEED_E_STR(namebuf.data));
                  appendStringLiteralConn(buf, namebuf.data, pset.db);
                  appendPQExpBuffer(buf, "\n        OR %s ~ ", altnamevar);
+                 appendPQExpBufferChar(buf, NEED_E_STR(namebuf.data));
                  appendStringLiteralConn(buf, namebuf.data, pset.db);
                  appendPQExpBuffer(buf, ")\n");
              }
              else
              {
                  appendPQExpBuffer(buf, "%s ~ ", namevar);
+                 appendPQExpBufferChar(buf, NEED_E_STR(namebuf.data));
                  appendStringLiteralConn(buf, namebuf.data, pset.db);
                  appendPQExpBufferChar(buf, '\n');
              }
***************
*** 1938,1943 ****
--- 1941,1947 ----
          {
              WHEREAND();
              appendPQExpBuffer(buf, "%s ~ ", schemavar);
+             appendPQExpBufferChar(buf, NEED_E_STR(schemabuf.data));
              appendStringLiteralConn(buf, schemabuf.data, pset.db);
              appendPQExpBufferChar(buf, '\n');
          }
Index: src/bin/scripts/createdb.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/scripts/createdb.c,v
retrieving revision 1.19
diff -c -c -r1.19 createdb.c
*** src/bin/scripts/createdb.c    29 May 2006 19:52:46 -0000    1.19
--- src/bin/scripts/createdb.c    29 May 2006 20:41:08 -0000
***************
*** 185,190 ****
--- 185,192 ----
      {
          conn = connectDatabase(dbname, host, port, username, password, progname);

+         executeCommand(conn, "SET escape_string_warning TO 'off'", progname, false);
+
          printfPQExpBuffer(&sql, "COMMENT ON DATABASE %s IS ", fmtId(dbname));
          appendStringLiteralConn(&sql, comment, conn);
          appendPQExpBuffer(&sql, ";\n");
Index: src/bin/scripts/createuser.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/scripts/createuser.c,v
retrieving revision 1.30
diff -c -c -r1.30 createuser.c
*** src/bin/scripts/createuser.c    29 May 2006 19:52:46 -0000    1.30
--- src/bin/scripts/createuser.c    29 May 2006 20:41:08 -0000
***************
*** 243,248 ****
--- 243,250 ----
      printfPQExpBuffer(&sql, "CREATE ROLE %s", fmtId(newuser));
      if (newpassword)
      {
+         executeCommand(conn, "SET escape_string_warning TO 'off'", progname, false);
+
          if (encrypted == TRI_YES)
              appendPQExpBuffer(&sql, " ENCRYPTED");
          if (encrypted == TRI_NO)
Index: src/bin/scripts/droplang.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/scripts/droplang.c,v
retrieving revision 1.20
diff -c -c -r1.20 droplang.c
*** src/bin/scripts/droplang.c    29 May 2006 19:52:46 -0000    1.20
--- src/bin/scripts/droplang.c    29 May 2006 20:41:09 -0000
***************
*** 176,183 ****
       * Force schema search path to be just pg_catalog, so that we don't have
       * to be paranoid about search paths below.
       */
!     executeCommand(conn, "SET search_path = pg_catalog;",
!                    progname, echo);

      /*
       * Make sure the language is installed and find the OIDs of the handler
--- 176,182 ----
       * Force schema search path to be just pg_catalog, so that we don't have
       * to be paranoid about search paths below.
       */
!     executeCommand(conn, "SET search_path = pg_catalog;", progname, echo);

      /*
       * Make sure the language is installed and find the OIDs of the handler

psql strings and ''

From
Bruce Momjian
Date:
Currently, psql single-quote argument strings can only embed single
quotes as \', not ''.  This is because while the main psqlscan.l loop
understands '', the subsections used for psql arguments, xslasharg,
doesn't.  This patch attempts to fix that.

However, I am not sure it is done right because I am not using the xe
and xq state values, like the main psql scanner code.  I assume we can
not use them because we are already in xslasharg, and can't add another
state here.

The unusual thing is that in my testing it worked anyway.

---------------------------------------------------------------------------

Bruce Momjian wrote:
> Tom Lane wrote:
> > Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > > Right.  I think the question is whether we want all psql strings to
> > > accept backslashes, and hence not support E'' at all for psql commands.
> > > I figured that made the most sense.
> >
> > I'm not convinced.  Wouldn't it be better if psql commands track the
> > backend syntax?  With standard_conforming_strings on, there will be two
> > ways to tell COPY you want a tab as a delimiter:
> >     DELIMITER '<actual tab char>'
> >     DELIMITER E'\t'
> > and in particular this will NOT do that:
> >     DELIMITER '\t'
>
> Well, I think it a little more confusing that just \copy.  What about \d
> and \set uses of backslashes.  Do they honor standard_conforming_strings
> too?  I assume you are saying they should.
>
> > If we keep '\t' as meaning tab in the \copy syntax then I think we're
> > going to cause confusion in the long run.  I think we should fix \copy
> > and related psql backslash commands to accept E'\t', and make sure that
> > the behavior is the same as the connected backend depending on what its
> > standard_conforming_strings setting is.
>
> OK, though this is going to mean that examples in the psql manual page
> are going to be different for different standard_conforming_strings
> settings:
>
>        testdb=> \set content '\'' `cat my_file.txt` '\''
>        testdb=> INSERT INTO my_table VALUES (:content);
>
> psql doesn't know '''' is about doubling single quotes in a string,
> though \copy does.  The major problem, I think, is that psql often
> follows the shell rules, rather than the SQL rules for most things.
>
> > There is a secondary, largely cosmetic question of whether psql should
> > attempt to prevent you from seeing escape_string_warning messages.
> > I personally have come to the conclusion that escape_string_warning is
> > probably not going to be on by default anyway ;-), and hence it's not
> > worth going to great extremes to prevent this, particularly if it breaks
> > the ability to use psql against pre-8.1 servers.
>
> It does break backward compatibility.
>
> --
>   Bruce Momjian   http://candle.pha.pa.us
>   EnterpriseDB    http://www.enterprisedb.com
>
>   + If your life is a hard drive, Christ can be your backup. +
>
> ---------------------------(end of broadcast)---------------------------
> TIP 4: Have you searched our list archives?
>
>                http://archives.postgresql.org
>

--
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.162
diff -c -c -r1.162 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml    26 May 2006 19:51:29 -0000    1.162
--- doc/src/sgml/ref/psql-ref.sgml    29 May 2006 17:49:43 -0000
***************
*** 2262,2268 ****
      copy the contents of a file into a table column. First load the file into a
      variable and then proceed as above.
  <programlisting>
! testdb=> <userinput>\set content '\'' `cat my_file.txt` '\''</userinput>
  testdb=> <userinput>INSERT INTO my_table VALUES (:content);</userinput>
  </programlisting>
      One possible problem with this approach is that <filename>my_file.txt</filename>
--- 2262,2268 ----
      copy the contents of a file into a table column. First load the file into a
      variable and then proceed as above.
  <programlisting>
! testdb=> <userinput>\set content '''' `cat my_file.txt` ''''</userinput>
  testdb=> <userinput>INSERT INTO my_table VALUES (:content);</userinput>
  </programlisting>
      One possible problem with this approach is that <filename>my_file.txt</filename>
***************
*** 2270,2283 ****
      they don't cause a syntax error when the second line is processed. This
      could be done with the program <command>sed</command>:
  <programlisting>
! testdb=> <userinput>\set content '\'' `sed -e "s/'/\\\\\\'/g" < my_file.txt` '\''</userinput>
  </programlisting>
      Observe the correct number of backslashes (6)! It works
      this way: After <application>psql</application> has parsed this
!     line, it passes <literal>sed -e "s/'/\\\'/g" < my_file.txt</literal>
      to the shell. The shell will do its own thing inside the double
      quotes and execute <command>sed</command> with the arguments
!     <literal>-e</literal> and <literal>s/'/\\'/g</literal>. When
      <command>sed</command> parses this it will replace the two
      backslashes with a single one and then do the substitution. Perhaps
      at one point you thought it was great that all Unix commands use the
--- 2270,2283 ----
      they don't cause a syntax error when the second line is processed. This
      could be done with the program <command>sed</command>:
  <programlisting>
! testdb=> <userinput>\set content '''' `sed -e "s/'/\\\\''/g" < my_file.txt` ''''</userinput>
  </programlisting>
      Observe the correct number of backslashes (6)! It works
      this way: After <application>psql</application> has parsed this
!     line, it passes <literal>sed -e "s/'/\\''/g" < my_file.txt</literal>
      to the shell. The shell will do its own thing inside the double
      quotes and execute <command>sed</command> with the arguments
!     <literal>-e</literal> and <literal>s/'/''/g</literal>. When
      <command>sed</command> parses this it will replace the two
      backslashes with a single one and then do the substitution. Perhaps
      at one point you thought it was great that all Unix commands use the
Index: src/bin/psql/psqlscan.l
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/psqlscan.l,v
retrieving revision 1.18
diff -c -c -r1.18 psqlscan.l
*** src/bin/psql/psqlscan.l    11 May 2006 19:15:35 -0000    1.18
--- src/bin/psql/psqlscan.l    29 May 2006 17:49:50 -0000
***************
*** 861,866 ****
--- 861,868 ----

  {quote}            { return LEXRES_OK; }

+ {xqdouble}        { emit("'", 1); }
+
  "\\n"            { appendPQExpBufferChar(output_buf, '\n'); }
  "\\t"            { appendPQExpBufferChar(output_buf, '\t'); }
  "\\b"            { appendPQExpBufferChar(output_buf, '\b'); }

Re: [HACKERS] psql \copy warning

From
Bruce Momjian
Date:
Patch applied.

---------------------------------------------------------------------------

Bruce Momjian wrote:
>
> I have developed an updated patch that:
>
>     o  turns off escape_string_warning in pg_dumpall.c
>     o  optionally use E'' for \password (undocumented option?)
>     o  honor standard_conforming-strings for \copy (but not
>        support literal E'' strings)
>     o  optionally use E'' for \d commands
>     o  turn off escape_string_warning for createdb, createuser,
>        droplang
>
> I agree someday we might want to turn off escape_string_warning, but I
> think we should leave it on as long as possible as it is still pointing
> out escape problem areas in the code.
>
> ---------------------------------------------------------------------------
>
> Bruce Momjian wrote:
> > Tom Lane wrote:
> > > Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > > > Right.  I think the question is whether we want all psql strings to
> > > > accept backslashes, and hence not support E'' at all for psql commands.
> > > > I figured that made the most sense.
> > >
> > > I'm not convinced.  Wouldn't it be better if psql commands track the
> > > backend syntax?  With standard_conforming_strings on, there will be two
> > > ways to tell COPY you want a tab as a delimiter:
> > >     DELIMITER '<actual tab char>'
> > >     DELIMITER E'\t'
> > > and in particular this will NOT do that:
> > >     DELIMITER '\t'
> >
> > Well, I think it a little more confusing that just \copy.  What about \d
> > and \set uses of backslashes.  Do they honor standard_conforming_strings
> > too?  I assume you are saying they should.
> >
> > > If we keep '\t' as meaning tab in the \copy syntax then I think we're
> > > going to cause confusion in the long run.  I think we should fix \copy
> > > and related psql backslash commands to accept E'\t', and make sure that
> > > the behavior is the same as the connected backend depending on what its
> > > standard_conforming_strings setting is.
> >
> > OK, though this is going to mean that examples in the psql manual page
> > are going to be different for different standard_conforming_strings
> > settings:
> >
> >        testdb=> \set content '\'' `cat my_file.txt` '\''
> >        testdb=> INSERT INTO my_table VALUES (:content);
> >
> > psql doesn't know '''' is about doubling single quotes in a string,
> > though \copy does.  The major problem, I think, is that psql often
> > follows the shell rules, rather than the SQL rules for most things.
> >
> > > There is a secondary, largely cosmetic question of whether psql should
> > > attempt to prevent you from seeing escape_string_warning messages.
> > > I personally have come to the conclusion that escape_string_warning is
> > > probably not going to be on by default anyway ;-), and hence it's not
> > > worth going to great extremes to prevent this, particularly if it breaks
> > > the ability to use psql against pre-8.1 servers.
> >
> > It does break backward compatibility.
> >
> > --
> >   Bruce Momjian   http://candle.pha.pa.us
> >   EnterpriseDB    http://www.enterprisedb.com
> >
> >   + If your life is a hard drive, Christ can be your backup. +
> >
> > ---------------------------(end of broadcast)---------------------------
> > TIP 4: Have you searched our list archives?
> >
> >                http://archives.postgresql.org
> >
>
> --
>   Bruce Momjian   http://candle.pha.pa.us
>   EnterpriseDB    http://www.enterprisedb.com
>
>   + If your life is a hard drive, Christ can be your backup. +

> Index: src/bin/pg_dump/pg_dumpall.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/bin/pg_dump/pg_dumpall.c,v
> retrieving revision 1.77
> diff -c -c -r1.77 pg_dumpall.c
> *** src/bin/pg_dump/pg_dumpall.c    28 May 2006 21:13:54 -0000    1.77
> --- src/bin/pg_dump/pg_dumpall.c    29 May 2006 20:41:01 -0000
> ***************
> *** 338,343 ****
> --- 338,345 ----
>           printf("SET client_encoding = '%s';\n",
>                  pg_encoding_to_char(encoding));
>           printf("SET standard_conforming_strings = %s;\n", std_strings);
> +         if (strcmp(std_strings, "off") == 0)
> +             printf("SET escape_string_warning = 'off';\n");
>           printf("\n");
>
>           /* Dump roles (users) */
> Index: src/bin/psql/command.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v
> retrieving revision 1.166
> diff -c -c -r1.166 command.c
> *** src/bin/psql/command.c    2 Apr 2006 20:08:22 -0000    1.166
> --- src/bin/psql/command.c    29 May 2006 20:41:02 -0000
> ***************
> *** 681,688 ****
>                   PGresult   *res;
>
>                   initPQExpBuffer(&buf);
> !                 printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD '%s';",
> !                                   fmtId(user), encrypted_password);
>                   res = PSQLexec(buf.data, false);
>                   termPQExpBuffer(&buf);
>                   if (!res)
> --- 681,689 ----
>                   PGresult   *res;
>
>                   initPQExpBuffer(&buf);
> !                 printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD %c'%s';",
> !                                   fmtId(user), NEED_E_STR(encrypted_password),
> !                                   encrypted_password);
>                   res = PSQLexec(buf.data, false);
>                   termPQExpBuffer(&buf);
>                   if (!res)
> Index: src/bin/psql/common.h
> ===================================================================
> RCS file: /cvsroot/pgsql/src/bin/psql/common.h,v
> retrieving revision 1.47
> diff -c -c -r1.47 common.h
> *** src/bin/psql/common.h    6 Mar 2006 19:49:20 -0000    1.47
> --- src/bin/psql/common.h    29 May 2006 20:41:03 -0000
> ***************
> *** 23,28 ****
> --- 23,34 ----
>   #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
>
>   /*
> +  *    We use this to prefix strings with E'' that we know are already safe,
> +  *    so we don't get an escape_string_warning.
> +  */
> + #define    NEED_E_STR(str)        ((strchr(str, '\\') && !standard_strings()) ? ESCAPE_STRING_SYNTAX : ' ')
> +
> + /*
>    * Safer versions of some standard C library functions. If an
>    * out-of-memory condition occurs, these functions will bail out
>    * safely; therefore, their return value is guaranteed to be non-NULL.
> Index: src/bin/psql/copy.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/bin/psql/copy.c,v
> retrieving revision 1.61
> diff -c -c -r1.61 copy.c
> *** src/bin/psql/copy.c    26 May 2006 19:51:29 -0000    1.61
> --- src/bin/psql/copy.c    29 May 2006 20:41:03 -0000
> ***************
> *** 216,222 ****
>           goto error;
>
>       token = strtokx(NULL, whitespace, NULL, "'",
> !                     '\\', true, pset.encoding);
>       if (!token)
>           goto error;
>
> --- 216,222 ----
>           goto error;
>
>       token = strtokx(NULL, whitespace, NULL, "'",
> !                     standard_strings() ? 0 : '\\', true, pset.encoding);
>       if (!token)
>           goto error;
>
> ***************
> *** 255,261 ****
>       if (token && pg_strcasecmp(token, "delimiters") == 0)
>       {
>           token = strtokx(NULL, whitespace, NULL, "'",
> !                         '\\', false, pset.encoding);
>           if (!token)
>               goto error;
>           result->delim = pg_strdup(token);
> --- 255,261 ----
>       if (token && pg_strcasecmp(token, "delimiters") == 0)
>       {
>           token = strtokx(NULL, whitespace, NULL, "'",
> !                         standard_strings() ? 0 : '\\', false, pset.encoding);
>           if (!token)
>               goto error;
>           result->delim = pg_strdup(token);
> ***************
> *** 290,299 ****
>               else if (pg_strcasecmp(token, "delimiter") == 0)
>               {
>                   token = strtokx(NULL, whitespace, NULL, "'",
> !                                 '\\', false, pset.encoding);
>                   if (token && pg_strcasecmp(token, "as") == 0)
>                       token = strtokx(NULL, whitespace, NULL, "'",
> !                                     '\\', false, pset.encoding);
>                   if (token)
>                       result->delim = pg_strdup(token);
>                   else
> --- 290,299 ----
>               else if (pg_strcasecmp(token, "delimiter") == 0)
>               {
>                   token = strtokx(NULL, whitespace, NULL, "'",
> !                                 standard_strings() ? 0 : '\\', false, pset.encoding);
>                   if (token && pg_strcasecmp(token, "as") == 0)
>                       token = strtokx(NULL, whitespace, NULL, "'",
> !                                     standard_strings() ? 0 : '\\', false, pset.encoding);
>                   if (token)
>                       result->delim = pg_strdup(token);
>                   else
> ***************
> *** 302,311 ****
>               else if (pg_strcasecmp(token, "null") == 0)
>               {
>                   token = strtokx(NULL, whitespace, NULL, "'",
> !                                 '\\', false, pset.encoding);
>                   if (token && pg_strcasecmp(token, "as") == 0)
>                       token = strtokx(NULL, whitespace, NULL, "'",
> !                                     '\\', false, pset.encoding);
>                   if (token)
>                       result->null = pg_strdup(token);
>                   else
> --- 302,311 ----
>               else if (pg_strcasecmp(token, "null") == 0)
>               {
>                   token = strtokx(NULL, whitespace, NULL, "'",
> !                                 standard_strings() ? 0 : '\\', false, pset.encoding);
>                   if (token && pg_strcasecmp(token, "as") == 0)
>                       token = strtokx(NULL, whitespace, NULL, "'",
> !                                     standard_strings() ? 0 : '\\', false, pset.encoding);
>                   if (token)
>                       result->null = pg_strdup(token);
>                   else
> ***************
> *** 314,323 ****
>               else if (pg_strcasecmp(token, "quote") == 0)
>               {
>                   token = strtokx(NULL, whitespace, NULL, "'",
> !                                 '\\', false, pset.encoding);
>                   if (token && pg_strcasecmp(token, "as") == 0)
>                       token = strtokx(NULL, whitespace, NULL, "'",
> !                                     '\\', false, pset.encoding);
>                   if (token)
>                       result->quote = pg_strdup(token);
>                   else
> --- 314,323 ----
>               else if (pg_strcasecmp(token, "quote") == 0)
>               {
>                   token = strtokx(NULL, whitespace, NULL, "'",
> !                                 standard_strings() ? 0 : '\\', false, pset.encoding);
>                   if (token && pg_strcasecmp(token, "as") == 0)
>                       token = strtokx(NULL, whitespace, NULL, "'",
> !                                     standard_strings() ? 0 : '\\', false, pset.encoding);
>                   if (token)
>                       result->quote = pg_strdup(token);
>                   else
> ***************
> *** 326,335 ****
>               else if (pg_strcasecmp(token, "escape") == 0)
>               {
>                   token = strtokx(NULL, whitespace, NULL, "'",
> !                                 '\\', false, pset.encoding);
>                   if (token && pg_strcasecmp(token, "as") == 0)
>                       token = strtokx(NULL, whitespace, NULL, "'",
> !                                     '\\', false, pset.encoding);
>                   if (token)
>                       result->escape = pg_strdup(token);
>                   else
> --- 326,335 ----
>               else if (pg_strcasecmp(token, "escape") == 0)
>               {
>                   token = strtokx(NULL, whitespace, NULL, "'",
> !                                 standard_strings() ? 0 : '\\', false, pset.encoding);
>                   if (token && pg_strcasecmp(token, "as") == 0)
>                       token = strtokx(NULL, whitespace, NULL, "'",
> !                                     standard_strings() ? 0 : '\\', false, pset.encoding);
>                   if (token)
>                       result->escape = pg_strdup(token);
>                   else
> ***************
> *** 462,481 ****
>       if (options->delim)
>       {
>           if (options->delim[0] == '\'')
> !             appendPQExpBuffer(&query, " USING DELIMITERS %s",
> !                               options->delim);
>           else
> !             appendPQExpBuffer(&query, " USING DELIMITERS '%s'",
> !                               options->delim);
>       }
>
>       /* There is no backward-compatible CSV syntax */
>       if (options->null)
>       {
>           if (options->null[0] == '\'')
> !             appendPQExpBuffer(&query, " WITH NULL AS %s", options->null);
>           else
> !             appendPQExpBuffer(&query, " WITH NULL AS '%s'", options->null);
>       }
>
>       if (options->csv_mode)
> --- 462,483 ----
>       if (options->delim)
>       {
>           if (options->delim[0] == '\'')
> !             appendPQExpBuffer(&query, " USING DELIMITERS %c%s",
> !                               NEED_E_STR(options->delim), options->delim);
>           else
> !             appendPQExpBuffer(&query, " USING DELIMITERS %c'%s'",
> !                               NEED_E_STR(options->delim), options->delim);
>       }
>
>       /* There is no backward-compatible CSV syntax */
>       if (options->null)
>       {
>           if (options->null[0] == '\'')
> !             appendPQExpBuffer(&query, " WITH NULL AS %c%s",
> !                               NEED_E_STR(options->null), options->null);
>           else
> !             appendPQExpBuffer(&query, " WITH NULL AS %c'%s'",
> !                               NEED_E_STR(options->null), options->null);
>       }
>
>       if (options->csv_mode)
> ***************
> *** 487,503 ****
>       if (options->quote)
>       {
>           if (options->quote[0] == '\'')
> !             appendPQExpBuffer(&query, " QUOTE AS %s", options->quote);
>           else
> !             appendPQExpBuffer(&query, " QUOTE AS '%s'", options->quote);
>       }
>
>       if (options->escape)
>       {
>           if (options->escape[0] == '\'')
> !             appendPQExpBuffer(&query, " ESCAPE AS %s", options->escape);
>           else
> !             appendPQExpBuffer(&query, " ESCAPE AS '%s'", options->escape);
>       }
>
>       if (options->force_quote_list)
> --- 489,509 ----
>       if (options->quote)
>       {
>           if (options->quote[0] == '\'')
> !             appendPQExpBuffer(&query, " QUOTE AS %c%s",
> !                               NEED_E_STR(options->quote), options->quote);
>           else
> !             appendPQExpBuffer(&query, " QUOTE AS %c'%s'",
> !                               NEED_E_STR(options->quote), options->quote);
>       }
>
>       if (options->escape)
>       {
>           if (options->escape[0] == '\'')
> !             appendPQExpBuffer(&query, " ESCAPE AS %c%s",
> !                               NEED_E_STR(options->escape), options->escape);
>           else
> !             appendPQExpBuffer(&query, " ESCAPE AS %c'%s'",
> !                               NEED_E_STR(options->escape), options->escape);
>       }
>
>       if (options->force_quote_list)
> Index: src/bin/psql/describe.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/bin/psql/describe.c,v
> retrieving revision 1.137
> diff -c -c -r1.137 describe.c
> *** src/bin/psql/describe.c    28 May 2006 21:13:54 -0000    1.137
> --- src/bin/psql/describe.c    29 May 2006 20:41:04 -0000
> ***************
> *** 1907,1920 ****
> --- 1907,1923 ----
>               if (altnamevar)
>               {
>                   appendPQExpBuffer(buf, "(%s ~ ", namevar);
> +                 appendPQExpBufferChar(buf, NEED_E_STR(namebuf.data));
>                   appendStringLiteralConn(buf, namebuf.data, pset.db);
>                   appendPQExpBuffer(buf, "\n        OR %s ~ ", altnamevar);
> +                 appendPQExpBufferChar(buf, NEED_E_STR(namebuf.data));
>                   appendStringLiteralConn(buf, namebuf.data, pset.db);
>                   appendPQExpBuffer(buf, ")\n");
>               }
>               else
>               {
>                   appendPQExpBuffer(buf, "%s ~ ", namevar);
> +                 appendPQExpBufferChar(buf, NEED_E_STR(namebuf.data));
>                   appendStringLiteralConn(buf, namebuf.data, pset.db);
>                   appendPQExpBufferChar(buf, '\n');
>               }
> ***************
> *** 1938,1943 ****
> --- 1941,1947 ----
>           {
>               WHEREAND();
>               appendPQExpBuffer(buf, "%s ~ ", schemavar);
> +             appendPQExpBufferChar(buf, NEED_E_STR(schemabuf.data));
>               appendStringLiteralConn(buf, schemabuf.data, pset.db);
>               appendPQExpBufferChar(buf, '\n');
>           }
> Index: src/bin/scripts/createdb.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/bin/scripts/createdb.c,v
> retrieving revision 1.19
> diff -c -c -r1.19 createdb.c
> *** src/bin/scripts/createdb.c    29 May 2006 19:52:46 -0000    1.19
> --- src/bin/scripts/createdb.c    29 May 2006 20:41:08 -0000
> ***************
> *** 185,190 ****
> --- 185,192 ----
>       {
>           conn = connectDatabase(dbname, host, port, username, password, progname);
>
> +         executeCommand(conn, "SET escape_string_warning TO 'off'", progname, false);
> +
>           printfPQExpBuffer(&sql, "COMMENT ON DATABASE %s IS ", fmtId(dbname));
>           appendStringLiteralConn(&sql, comment, conn);
>           appendPQExpBuffer(&sql, ";\n");
> Index: src/bin/scripts/createuser.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/bin/scripts/createuser.c,v
> retrieving revision 1.30
> diff -c -c -r1.30 createuser.c
> *** src/bin/scripts/createuser.c    29 May 2006 19:52:46 -0000    1.30
> --- src/bin/scripts/createuser.c    29 May 2006 20:41:08 -0000
> ***************
> *** 243,248 ****
> --- 243,250 ----
>       printfPQExpBuffer(&sql, "CREATE ROLE %s", fmtId(newuser));
>       if (newpassword)
>       {
> +         executeCommand(conn, "SET escape_string_warning TO 'off'", progname, false);
> +
>           if (encrypted == TRI_YES)
>               appendPQExpBuffer(&sql, " ENCRYPTED");
>           if (encrypted == TRI_NO)
> Index: src/bin/scripts/droplang.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/bin/scripts/droplang.c,v
> retrieving revision 1.20
> diff -c -c -r1.20 droplang.c
> *** src/bin/scripts/droplang.c    29 May 2006 19:52:46 -0000    1.20
> --- src/bin/scripts/droplang.c    29 May 2006 20:41:09 -0000
> ***************
> *** 176,183 ****
>        * Force schema search path to be just pg_catalog, so that we don't have
>        * to be paranoid about search paths below.
>        */
> !     executeCommand(conn, "SET search_path = pg_catalog;",
> !                    progname, echo);
>
>       /*
>        * Make sure the language is installed and find the OIDs of the handler
> --- 176,182 ----
>        * Force schema search path to be just pg_catalog, so that we don't have
>        * to be paranoid about search paths below.
>        */
> !     executeCommand(conn, "SET search_path = pg_catalog;", progname, echo);
>
>       /*
>        * Make sure the language is installed and find the OIDs of the handler

>
> ---------------------------(end of broadcast)---------------------------
> TIP 9: In versions below 8.0, the planner will ignore your desire to
>        choose an index scan if your joining column's datatypes do not
>        match

--
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: psql strings and ''

From
Bruce Momjian
Date:
Patch applied.  Thanks.

I see now that a state is not required because we are already in the
single-quote string at that point, comment added.

---------------------------------------------------------------------------


Bruce Momjian wrote:
>
> Currently, psql single-quote argument strings can only embed single
> quotes as \', not ''.  This is because while the main psqlscan.l loop
> understands '', the subsections used for psql arguments, xslasharg,
> doesn't.  This patch attempts to fix that.
>
> However, I am not sure it is done right because I am not using the xe
> and xq state values, like the main psql scanner code.  I assume we can
> not use them because we are already in xslasharg, and can't add another
> state here.
>
> The unusual thing is that in my testing it worked anyway.
>
> ---------------------------------------------------------------------------
>
> Bruce Momjian wrote:
> > Tom Lane wrote:
> > > Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > > > Right.  I think the question is whether we want all psql strings to
> > > > accept backslashes, and hence not support E'' at all for psql commands.
> > > > I figured that made the most sense.
> > >
> > > I'm not convinced.  Wouldn't it be better if psql commands track the
> > > backend syntax?  With standard_conforming_strings on, there will be two
> > > ways to tell COPY you want a tab as a delimiter:
> > >     DELIMITER '<actual tab char>'
> > >     DELIMITER E'\t'
> > > and in particular this will NOT do that:
> > >     DELIMITER '\t'
> >
> > Well, I think it a little more confusing that just \copy.  What about \d
> > and \set uses of backslashes.  Do they honor standard_conforming_strings
> > too?  I assume you are saying they should.
> >
> > > If we keep '\t' as meaning tab in the \copy syntax then I think we're
> > > going to cause confusion in the long run.  I think we should fix \copy
> > > and related psql backslash commands to accept E'\t', and make sure that
> > > the behavior is the same as the connected backend depending on what its
> > > standard_conforming_strings setting is.
> >
> > OK, though this is going to mean that examples in the psql manual page
> > are going to be different for different standard_conforming_strings
> > settings:
> >
> >        testdb=> \set content '\'' `cat my_file.txt` '\''
> >        testdb=> INSERT INTO my_table VALUES (:content);
> >
> > psql doesn't know '''' is about doubling single quotes in a string,
> > though \copy does.  The major problem, I think, is that psql often
> > follows the shell rules, rather than the SQL rules for most things.
> >
> > > There is a secondary, largely cosmetic question of whether psql should
> > > attempt to prevent you from seeing escape_string_warning messages.
> > > I personally have come to the conclusion that escape_string_warning is
> > > probably not going to be on by default anyway ;-), and hence it's not
> > > worth going to great extremes to prevent this, particularly if it breaks
> > > the ability to use psql against pre-8.1 servers.
> >
> > It does break backward compatibility.
> >
> > --
> >   Bruce Momjian   http://candle.pha.pa.us
> >   EnterpriseDB    http://www.enterprisedb.com
> >
> >   + If your life is a hard drive, Christ can be your backup. +
> >
> > ---------------------------(end of broadcast)---------------------------
> > TIP 4: Have you searched our list archives?
> >
> >                http://archives.postgresql.org
> >
>
> --
>   Bruce Momjian   http://candle.pha.pa.us
>   EnterpriseDB    http://www.enterprisedb.com
>
>   + If your life is a hard drive, Christ can be your backup. +

> Index: doc/src/sgml/ref/psql-ref.sgml
> ===================================================================
> RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
> retrieving revision 1.162
> diff -c -c -r1.162 psql-ref.sgml
> *** doc/src/sgml/ref/psql-ref.sgml    26 May 2006 19:51:29 -0000    1.162
> --- doc/src/sgml/ref/psql-ref.sgml    29 May 2006 17:49:43 -0000
> ***************
> *** 2262,2268 ****
>       copy the contents of a file into a table column. First load the file into a
>       variable and then proceed as above.
>   <programlisting>
> ! testdb=> <userinput>\set content '\'' `cat my_file.txt` '\''</userinput>
>   testdb=> <userinput>INSERT INTO my_table VALUES (:content);</userinput>
>   </programlisting>
>       One possible problem with this approach is that <filename>my_file.txt</filename>
> --- 2262,2268 ----
>       copy the contents of a file into a table column. First load the file into a
>       variable and then proceed as above.
>   <programlisting>
> ! testdb=> <userinput>\set content '''' `cat my_file.txt` ''''</userinput>
>   testdb=> <userinput>INSERT INTO my_table VALUES (:content);</userinput>
>   </programlisting>
>       One possible problem with this approach is that <filename>my_file.txt</filename>
> ***************
> *** 2270,2283 ****
>       they don't cause a syntax error when the second line is processed. This
>       could be done with the program <command>sed</command>:
>   <programlisting>
> ! testdb=> <userinput>\set content '\'' `sed -e "s/'/\\\\\\'/g" < my_file.txt` '\''</userinput>
>   </programlisting>
>       Observe the correct number of backslashes (6)! It works
>       this way: After <application>psql</application> has parsed this
> !     line, it passes <literal>sed -e "s/'/\\\'/g" < my_file.txt</literal>
>       to the shell. The shell will do its own thing inside the double
>       quotes and execute <command>sed</command> with the arguments
> !     <literal>-e</literal> and <literal>s/'/\\'/g</literal>. When
>       <command>sed</command> parses this it will replace the two
>       backslashes with a single one and then do the substitution. Perhaps
>       at one point you thought it was great that all Unix commands use the
> --- 2270,2283 ----
>       they don't cause a syntax error when the second line is processed. This
>       could be done with the program <command>sed</command>:
>   <programlisting>
> ! testdb=> <userinput>\set content '''' `sed -e "s/'/\\\\''/g" < my_file.txt` ''''</userinput>
>   </programlisting>
>       Observe the correct number of backslashes (6)! It works
>       this way: After <application>psql</application> has parsed this
> !     line, it passes <literal>sed -e "s/'/\\''/g" < my_file.txt</literal>
>       to the shell. The shell will do its own thing inside the double
>       quotes and execute <command>sed</command> with the arguments
> !     <literal>-e</literal> and <literal>s/'/''/g</literal>. When
>       <command>sed</command> parses this it will replace the two
>       backslashes with a single one and then do the substitution. Perhaps
>       at one point you thought it was great that all Unix commands use the
> Index: src/bin/psql/psqlscan.l
> ===================================================================
> RCS file: /cvsroot/pgsql/src/bin/psql/psqlscan.l,v
> retrieving revision 1.18
> diff -c -c -r1.18 psqlscan.l
> *** src/bin/psql/psqlscan.l    11 May 2006 19:15:35 -0000    1.18
> --- src/bin/psql/psqlscan.l    29 May 2006 17:49:50 -0000
> ***************
> *** 861,866 ****
> --- 861,868 ----
>
>   {quote}            { return LEXRES_OK; }
>
> + {xqdouble}        { emit("'", 1); }
> +
>   "\\n"            { appendPQExpBufferChar(output_buf, '\n'); }
>   "\\t"            { appendPQExpBufferChar(output_buf, '\t'); }
>   "\\b"            { appendPQExpBufferChar(output_buf, '\b'); }

>
> ---------------------------(end of broadcast)---------------------------
> TIP 3: Have you checked our extensive FAQ?
>
>                http://www.postgresql.org/docs/faq

--
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: [HACKERS] psql \copy warning

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
>> I have developed an updated patch that:
>>
>> o  turns off escape_string_warning in pg_dumpall.c
>> o  optionally use E'' for \password (undocumented option?)
>> o  honor standard_conforming-strings for \copy (but not
>> support literal E'' strings)
>> o  optionally use E'' for \d commands
>> o  turn off escape_string_warning for createdb, createuser,
>> droplang
>>
>> I agree someday we might want to turn off escape_string_warning, but I
>> think we should leave it on as long as possible as it is still pointing
>> out escape problem areas in the code.

I find this patch mighty ugly, and hope that most of it can get reverted
before 8.2.  The changes you made in describe.c are actively broken ...
didn't you test it?  appendStringLiteralConn doesn't know what you did.

I think that a far saner approach would be to make all these places use
appendStringLiteralConn, and to change *only* that routine to throw on
an E at need.  That would (a) not be broken, and (b) give us just one
place to change the behavior when it comes time.

            regards, tom lane

Re: [HACKERS] psql \copy warning

From
Bruce Momjian
Date:
Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> >> I have developed an updated patch that:
> >>
> >> o  turns off escape_string_warning in pg_dumpall.c
> >> o  optionally use E'' for \password (undocumented option?)
> >> o  honor standard_conforming-strings for \copy (but not
> >> support literal E'' strings)
> >> o  optionally use E'' for \d commands
> >> o  turn off escape_string_warning for createdb, createuser,
> >> droplang
> >>
> >> I agree someday we might want to turn off escape_string_warning, but I
> >> think we should leave it on as long as possible as it is still pointing
> >> out escape problem areas in the code.
>
> I find this patch mighty ugly, and hope that most of it can get reverted
> before 8.2.  The changes you made in describe.c are actively broken ...
> didn't you test it?  appendStringLiteralConn doesn't know what you did.

I will look into that.  Thanks.

> I think that a far saner approach would be to make all these places use
> appendStringLiteralConn, and to change *only* that routine to throw on
> an E at need.  That would (a) not be broken, and (b) give us just one
> place to change the behavior when it comes time.

The problem is that pg_dump uses appendStringLiteralConn() too, and you
didn't want pg_dump to emit E'', so you would have to add a boolean to
appendStringLiteralConn() to say whether you want an optional E'' and
that seems even uglier.

--
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +