Add new escaping functions PQescapeLiteral and PQescapeIdentifier.
authorRobert Haas
Thu, 21 Jan 2010 14:58:53 +0000 (14:58 +0000)
committerRobert Haas
Thu, 21 Jan 2010 14:58:53 +0000 (14:58 +0000)
PQescapeLiteral is similar to PQescapeStringConn, but it relieves the
caller of the need to know how large the output buffer should be, and
it provides the appropriate quoting (in addition to escaping special
characers within the string).  PQescapeIdentifier provides similar
functionality for escaping identifiers.

Per recent discussion with Tom Lane.

doc/src/sgml/libpq.sgml
src/interfaces/libpq/exports.txt
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/libpq-fe.h

index a5386cf333d92470583a1bd655cfc3c918c232d5..1d13e8b0b4e639e6ad9441f4ebba237905740f75 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
  <application>libpq</application> - C Library
@@ -2933,20 +2933,48 @@ typedef struct {
    
     
      
-      PQescapeStringConn
+      PQescapeLiteral
       
-       PQescapeStringConn
+       PQescapeLiteral
       
      
 
      
      
-      PQescapeStringConn escapes a string for
+      
+       size_t PQescapeLiteral(PGconn *conn, char *str, size_t len)
+      
+     
+
+     
+      PQescapeLiteral escapes a string for
       use within an SQL command.  This is useful when inserting data
       values as literal constants in SQL commands.  Certain characters
       (such as quotes and backslashes) must be escaped to prevent them
       from being interpreted specially by the SQL parser.
-      PQescapeStringConn performs this operation.
+      PQescapeLiteral performs this operation.
+     
+
+     
+      PQescapeLiteral returns an escaped version of the
+      str parameter in memory allocated with
+      malloc().  This memory should be freed using
+      PQfreemem() when the result is no longer needed.
+      A terminating zero byte is not required, and should not be
+      counted in length.  (If a terminating zero byte is found
+      before length bytes are processed,
+      PQescapeLiteral stops at the zero; the behavior is
+      thus rather like strncpy.) The
+      return string has all special characters replaced so that they can
+      be properly processed by the PostgreSQL
+      string literal parser.  A terminating zero byte is also added.  The
+      single quotes that must surround PostgreSQL
+      string literals are included in the result string.
+     
+
+     
+      On error, PQescapeLiteral returns NULL and a suitable
+      message is stored in the conn object.
      
 
      
@@ -2963,7 +2991,75 @@ typedef struct {
       Note that it is not necessary nor correct to do escaping when a data
       value is passed as a separate parameter in PQexecParams or
       its sibling routines.
+     
+     
+    
+
+    
+     
+      PQescapeIdentifier
+      
+       PQescapeIdentifier
+      
+     
+
+     
+     
+      
+       size_t PQescapeIdentifier(PGconn *conn, char *str, size_t len)
+      
+     
+
+     
+      PQescapeIndentifier escapes a string for
+      use as an SQL identifier, such as a table, column, or function name.
+      This is useful when a user-supplied identifier might contain
+      special characters that would otherwise not be interpreted as part
+      of the identifier by the SQL parser, or when the identifier might
+      contain uppercase characters whose case should be preserved.
+     
 
+     
+      PQescapeIdentifier returns a version of the
+      str parameter escaped as an SQL identifier
+      in memory allocated with malloc().  This memory must be
+      freed using PQfreemem() when the result is no longer
+      needed.  A terminating zero byte is not required, and should not be
+      counted in length.  (If a terminating zero byte is found
+      before length bytes are processed,
+      PQescapeIdentifier stops at the zero; the behavior is
+      thus rather like strncpy.) The
+      return string has all special characters replaced so that it
+      will be properly processed as an SQL identifier.  A terminating zero byte
+      is also added.  The return string will also be surrounded by double
+      quotes.
+     
+
+     
+      On error, PQescapeIdentifier returns NULL and a suitable
+      message is stored in the conn object.
+     
+
+     
+      
+       As with string literals, to prevent SQL injection attacks,
+       SQL identifiers must be escaped when they are received from an
+       untrustworthy source.
+      
+     
+     
+    
+
+    
+     
+      PQescapeStringConn
+      
+       PQescapeStringConn
+      
+     
+
+     
+     
       
        size_t PQescapeStringConn (PGconn *conn,
                                   char *to, const char *from, size_t length,
@@ -2972,12 +3068,12 @@ typedef struct {
      
 
      
-      PQescapeStringConn writes an escaped version of the
-      <parameter>from string to the to buffer, escaping
-      special characters so that they cannot cause any harm, and adding a
-      terminating zero byte.  The single quotes that must surround
-      PostgreSQL string literals are not included in the
-      result string; they should be provided in the SQL command that the
+      PQescapeStringConn escapes string literals, much like
+      <function>PQescapeLiteral.  Unlike PQescapeLiteral,
+      the caller is responsible for providing an appropriately sized buffer.
+      Furthermore, PQescapeStringConn does not generate the
+      single quotes that must surround PostgreSQL string
+      literals; they should be provided in the SQL command that the
       result is inserted into.  The parameter from points to
       the first character of the string that is to be escaped, and the
       length parameter gives the number of bytes in this
@@ -3093,7 +3189,7 @@ typedef struct {
       
        PQescapeByteaConn returns an escaped version of the
        from parameter binary string in memory
-       allocated with malloc().  This memory must be freed using
+       allocated with malloc().  This memory should be freed using
        PQfreemem() when the result is no longer needed.  The
        return string has all special characters replaced so that they can
        be properly processed by the PostgreSQL
@@ -4285,7 +4381,7 @@ typedef struct {
        be non-NULL.  *buffer is set to
        point to the allocated memory, or to NULL in cases
        where no buffer is returned.  A non-NULL result
-       buffer must be freed using PQfreemem when no longer
+       buffer should be freed using PQfreemem when no longer
        needed.
       
 
index f08fc58109e0b347eed8d14d4a5ee22b09105e4c..a7a1b94e111ed0b81b55dbe0d939bbe68f579779 100644 (file)
@@ -1,4 +1,4 @@
-# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.23 2009/03/31 01:41:27 tgl Exp $
+# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.24 2010/01/21 14:58:53 rhaas Exp $
 # Functions to be exported by libpq DLLs
 PQconnectdb               1
 PQsetdbLogin              2
@@ -153,3 +153,5 @@ PQresultSetInstanceData   150
 PQfireResultCreateEvents  151
 PQconninfoParse           152
 PQinitOpenSSL             153
+PQescapeLiteral           154
+PQescapeIdentifier        155
index 9fb358fc7080606b3348238d75e6be61a9d41d47..bbcf11a687a1304cc9d0599ee7de09328d57908d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.206 2010/01/02 16:58:12 momjian Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.207 2010/01/21 14:58:53 rhaas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3059,6 +3059,144 @@ PQescapeString(char *to, const char *from, size_t length)
 }
 
 
+/*
+ * Escape arbitrary strings.  If as_ident is true, we escape the result
+ * as an identifier; if false, as a literal.  The result is returned in
+ * a newly allocated buffer.  If we fail due to an encoding violation or out
+ * of memory condition, we return NULL, storing an error message into conn.
+ */
+static char *
+PQescapeInternal(PGconn *conn, const char *str, size_t len, int as_ident)
+{
+   const char *s;
+   char   *result;
+   char   *rp;
+   int     num_quotes = 0;     /* single or double, depending on as_ident */
+   int     num_backslashes = 0;
+   int     input_len;
+   int     result_size;
+   char    quote_char = as_ident ? '"' : '\'';
+
+   /* We must have a connection, else fail immediately. */
+   if (!conn)
+       return NULL;
+
+   /* Scan the string for characters that must be escaped. */
+   for (s = str; *s != '\0' && (s - str) < len; ++s)
+   {
+       if (*s == quote_char)
+           ++num_quotes;
+       else if (*s == '\\')
+           ++num_backslashes;
+       else if (IS_HIGHBIT_SET(*s))
+       {
+           int charlen;
+
+           /* Slow path for possible multibyte characters */
+           charlen = pg_encoding_mblen(conn->client_encoding, s);
+
+           /* Multibyte character overruns allowable length. */
+           if ((s - str) + charlen > len || memchr(s, 0, charlen) != NULL)
+           {
+               printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("incomplete multibyte character\n"));
+               return NULL;
+           }
+
+           /* Adjust s, bearing in mind that for loop will increment it. */
+           s += charlen - 1;
+       }
+   }
+
+   /* Allocate output buffer. */
+   input_len = s - str;
+   result_size = input_len + num_quotes + 3;  /* two quotes, plus a NUL */
+   if (!as_ident && num_backslashes > 0)
+       result_size += num_backslashes + 2;
+   result = rp = (char *) malloc(result_size);
+   if (rp == NULL)
+   {
+       printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("out of memory\n"));
+       return NULL;
+   }
+
+   /*
+    * If we are escaping a literal that contains backslashes, we use the
+    * escape string syntax so that the result is correct under either value
+    * of standard_conforming_strings.  We also emit a leading space in this
+    * case, to guard against the possibility that the result might be
+    * interpolated immediately following an identifier.
+    */
+   if (!as_ident && num_backslashes > 0)
+   {
+       *rp++ = ' ';
+       *rp++ = 'E';
+   }
+
+   /* Opening quote. */
+   *rp++ = quote_char;
+
+   /*
+    * Use fast path if possible.
+    *
+    * We've already verified that the input string is well-formed in the
+    * current encoding.  If it contains no quotes and, in the case of
+    * literal-escaping, no backslashes, then we can just copy it directly
+    * to the output buffer, adding the necessary quotes.
+    *
+    * If not, we must rescan the input and process each character
+    * individually.
+    */
+   if (num_quotes == 0 && (num_backslashes == 0 || as_ident))
+   {
+       memcpy(rp, str, input_len);
+       rp += input_len;
+   }
+   else
+   {
+       for (s = str; s - str < input_len; ++s)
+       {
+           if (*s == quote_char || (!as_ident && *s == '\\'))
+           {
+               *rp++ = *s;
+               *rp++ = *s;
+           }
+           else if (!IS_HIGHBIT_SET(*s))
+               *rp++ = *s;
+           else
+           {
+               int i = pg_encoding_mblen(conn->client_encoding, s);
+               while (1)
+               {
+                   *rp++ = *s;
+                   if (--i == 0)
+                       break;
+                   ++s;    /* for loop will provide the final increment */
+               }
+           }
+       }
+   }
+
+   /* Closing quote and terminating NUL. */
+   *rp++ = quote_char;
+   *rp = '\0';
+
+   return result;
+}
+
+char *
+PQescapeLiteral(PGconn *conn, const char *str, size_t len)
+{
+   return PQescapeInternal(conn, str, len, 0);
+}
+
+char *
+PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
+{
+   return PQescapeInternal(conn, str, len, 1);
+}
+
 /* HEX encoding support for bytea */
 static const char hextbl[] = "0123456789abcdef";
 
index 4c4dcec6b087d71050e5537be17ebdbf31268cbd..537bd231e8ff872487eddf63631998e8b30c84da 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.148 2010/01/02 16:58:12 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.149 2010/01/21 14:58:53 rhaas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -471,6 +471,8 @@ extern int  PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, in
 extern size_t PQescapeStringConn(PGconn *conn,
                   char *to, const char *from, size_t length,
                   int *error);
+extern char *PQescapeLiteral(PGconn *conn, const char *str, size_t len);
+extern char *PQescapeIdentifier(PGconn *conn, const char *str, size_t len);
 extern unsigned char *PQescapeByteaConn(PGconn *conn,
                  const unsigned char *from, size_t from_length,
                  size_t *to_length);