aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2010-01-21 14:58:53 +0000
committerRobert Haas <rhaas@postgresql.org>2010-01-21 14:58:53 +0000
commitd66679672f22de7360514f26cb81ccdb5ab43096 (patch)
treedc24170dbbf61be46e9e55dbb9315cbe3a22cb96 /src
parented1d3f5ecf60be7f8e7aec38657292f2455b4877 (diff)
downloadpostgresql-d66679672f22de7360514f26cb81ccdb5ab43096.tar.gz
postgresql-d66679672f22de7360514f26cb81ccdb5ab43096.zip
Add new escaping functions PQescapeLiteral and PQescapeIdentifier.
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.
Diffstat (limited to 'src')
-rw-r--r--src/interfaces/libpq/exports.txt4
-rw-r--r--src/interfaces/libpq/fe-exec.c140
-rw-r--r--src/interfaces/libpq/libpq-fe.h4
3 files changed, 145 insertions, 3 deletions
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index f08fc58109e..a7a1b94e111 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -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
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 9fb358fc708..bbcf11a687a 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -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";
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 4c4dcec6b08..537bd231e8f 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -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);