diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/interfaces/libpq/blibpqdll.def | 4 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 13 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-exec.c | 204 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-fe.h | 15 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-int.h | 3 | ||||
-rw-r--r-- | src/interfaces/libpq/libpqdll.def | 2 |
6 files changed, 188 insertions, 53 deletions
diff --git a/src/interfaces/libpq/blibpqdll.def b/src/interfaces/libpq/blibpqdll.def index eb78e770f8a..ac9ccd2b3f7 100644 --- a/src/interfaces/libpq/blibpqdll.def +++ b/src/interfaces/libpq/blibpqdll.def @@ -113,6 +113,8 @@ EXPORTS _PQfformat @ 109 _PQexecPrepared @ 110 _PQsendQueryPrepared @ 111 + _PQescapeStringConn @ 126 + _PQescapeByteaConn @ 127 ; Aliases for MS compatible names PQconnectdb = _PQconnectdb @@ -226,3 +228,5 @@ EXPORTS PQfformat = _PQfformat PQexecPrepared = _PQexecPrepared PQsendQueryPrepared = _PQsendQueryPrepared + PQescapeStringConn = _PQescapeStringConn + PQescapeByteaConn = _PQescapeByteaConn diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 4871ab96fe1..d9976f3fcff 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.263.2.4 2005/07/14 14:07:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.263.2.5 2006/05/21 20:20:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1956,6 +1956,7 @@ makeEmptyPGconn(void) conn->xactStatus = PQTRANS_IDLE; conn->setenv_state = SETENV_STATE_IDLE; conn->client_encoding = PG_SQL_ASCII; + conn->std_strings = false; /* unless server says differently */ conn->verbosity = PQERRORS_DEFAULT; conn->notifyList = DLNewList(); conn->sock = -1; @@ -2946,8 +2947,14 @@ PQsetClientEncoding(PGconn *conn, const char *encoding) status = -1; else { - /* change libpq internal encoding */ - conn->client_encoding = pg_char_to_encoding(encoding); + /* + * In protocol 2 we have to assume the setting will stick, and + * adjust our state immediately. In protocol 3 and up we can + * rely on the backend to report the parameter value, and we'll + * change state at that time. + */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + pqSaveParameterStatus(conn, "client_encoding", encoding); status = 0; /* everything is ok */ } PQclear(res); diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index e7cbc08d62b..325edb8d8ff 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.153.2.5 2006/05/21 19:57:07 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.153.2.6 2006/05/21 20:20:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -41,6 +41,12 @@ char *const pgresStatus[] = { "PGRES_FATAL_ERROR" }; +/* + * static state needed by PQescapeString and PQescapeBytea; initialize to + * values that result in backward-compatible behavior + */ +static int static_client_encoding = PG_SQL_ASCII; +static bool static_std_strings = false; static bool PQsendQueryStart(PGconn *conn); @@ -608,11 +614,22 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value) } /* - * Special hacks: remember client_encoding as a numeric value, and - * convert server version to a numeric form as well. + * Special hacks: remember client_encoding and standard_conforming_strings, + * and convert server version to a numeric form. We keep the first two of + * these in static variables as well, so that PQescapeString and + * PQescapeBytea can behave somewhat sanely (at least in single- + * connection-using programs). */ if (strcmp(name, "client_encoding") == 0) + { conn->client_encoding = pg_char_to_encoding(value); + static_client_encoding = conn->client_encoding; + } + else if (strcmp(name, "standard_conforming_strings") == 0) + { + conn->std_strings = (strcmp(value, "on") == 0); + static_std_strings = conn->std_strings; + } else if (strcmp(name, "server_version") == 0) { int cnt; @@ -2227,7 +2244,7 @@ PQfreeNotify(PGnotify *notify) /* * Escaping arbitrary strings to get valid SQL literal strings. * - * Replaces "\\" with "\\\\" and "'" with "''". + * Replaces "'" with "''", and if not std_strings, replaces "\" with "\\". * * length is the length of the source string. (Note: if a terminating NUL * is encountered sooner, PQescapeString stops short of "length"; the behavior @@ -2239,33 +2256,74 @@ PQfreeNotify(PGnotify *notify) * * Returns the actual length of the output (not counting the terminating NUL). */ -size_t -PQescapeString(char *to, const char *from, size_t length) +static size_t +PQescapeStringInternal(PGconn *conn, + char *to, const char *from, size_t length, + int *error, + int encoding, bool std_strings) { const char *source = from; char *target = to; size_t remaining = length; + if (error) + *error = 0; + while (remaining > 0 && *source != '\0') { - switch (*source) + char c = *source; + int len; + int i; + + /* Fast path for plain ASCII */ + if (!IS_HIGHBIT_SET(c)) { - case '\\': - *target++ = '\\'; - *target++ = '\\'; - break; + /* Apply quoting if needed */ + if (c == '\'' || + (c == '\\' && !std_strings)) + *target++ = c; + /* Copy the character */ + *target++ = c; + source++; + remaining--; + continue; + } - case '\'': - *target++ = '\''; - *target++ = '\''; - break; + /* Slow path for possible multibyte characters */ + len = pg_encoding_mblen(encoding, source); - default: - *target++ = *source; + /* Copy the character */ + for (i = 0; i < len; i++) + { + if (remaining == 0 || *source == '\0') break; + *target++ = *source++; + remaining--; + } + + /* + * If we hit premature end of string (ie, incomplete multibyte + * character), try to pad out to the correct length with spaces. + * We may not be able to pad completely, but we will always be able + * to insert at least one pad space (since we'd not have quoted a + * multibyte character). This should be enough to make a string that + * the server will error out on. + */ + if (i < len) + { + if (error) + *error = 1; + if (conn) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("incomplete multibyte character\n")); + for (; i < len; i++) + { + if (((size_t) (target - to)) / 2 >= length) + break; + *target++ = ' '; + } + break; } - source++; - remaining--; } /* Write the terminating NUL character. */ @@ -2274,72 +2332,109 @@ PQescapeString(char *to, const char *from, size_t length) return target - to; } +size_t +PQescapeStringConn(PGconn *conn, + char *to, const char *from, size_t length, + int *error) +{ + if (!conn) + { + /* force empty-string result */ + *to = '\0'; + if (error) + *error = 1; + return 0; + } + return PQescapeStringInternal(conn, to, from, length, error, + conn->client_encoding, + conn->std_strings); +} + +size_t +PQescapeString(char *to, const char *from, size_t length) +{ + return PQescapeStringInternal(NULL, to, from, length, NULL, + static_client_encoding, + static_std_strings); +} + /* * PQescapeBytea - converts from binary string to the * minimal encoding necessary to include the string in an SQL * INSERT statement with a bytea type column as the target. * * The following transformations are applied - * '\0' == ASCII 0 == \\000 - * '\'' == ASCII 39 == \' - * '\\' == ASCII 92 == \\\\ - * anything < 0x20, or > 0x7e ---> \\ooo - * (where ooo is an octal expression) + * '\0' == ASCII 0 == \000 + * '\'' == ASCII 39 == '' + * '\\' == ASCII 92 == \\ + * anything < 0x20, or > 0x7e ---> \ooo + * (where ooo is an octal expression) + * If not std_strings, all backslashes sent to the output are doubled. */ -unsigned char * -PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen) +static unsigned char * +PQescapeByteaInternal(PGconn *conn, + const unsigned char *from, size_t from_length, + size_t *to_length, bool std_strings) { const unsigned char *vp; unsigned char *rp; unsigned char *result; size_t i; size_t len; + size_t bslash_len = (std_strings ? 1 : 2); /* * empty string has 1 char ('\0') */ len = 1; - vp = bintext; - for (i = binlen; i > 0; i--, vp++) + vp = from; + for (i = from_length; i > 0; i--, vp++) { if (*vp < 0x20 || *vp > 0x7e) - len += 5; /* '5' is for '\\ooo' */ + len += bslash_len + 3; else if (*vp == '\'') len += 2; else if (*vp == '\\') - len += 4; + len += bslash_len + bslash_len; else len++; } + *to_length = len; rp = result = (unsigned char *) malloc(len); if (rp == NULL) + { + if (conn) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); return NULL; + } - vp = bintext; - *bytealen = len; - - for (i = binlen; i > 0; i--, vp++) + vp = from; + for (i = from_length; i > 0; i--, vp++) { if (*vp < 0x20 || *vp > 0x7e) { - (void) sprintf(rp, "\\\\%03o", *vp); - rp += 5; + if (!std_strings) + *rp++ = '\\'; + (void) sprintf((char *) rp, "\\%03o", *vp); + rp += 4; } else if (*vp == '\'') { - rp[0] = '\''; - rp[1] = '\''; - rp += 2; + *rp++ = '\''; + *rp++ = '\''; } else if (*vp == '\\') { - rp[0] = '\\'; - rp[1] = '\\'; - rp[2] = '\\'; - rp[3] = '\\'; - rp += 4; + if (!std_strings) + { + *rp++ = '\\'; + *rp++ = '\\'; + } + *rp++ = '\\'; + *rp++ = '\\'; } else *rp++ = *vp; @@ -2349,6 +2444,25 @@ PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen) return result; } +unsigned char * +PQescapeByteaConn(PGconn *conn, + const unsigned char *from, size_t from_length, + size_t *to_length) +{ + if (!conn) + return NULL; + return PQescapeByteaInternal(conn, from, from_length, to_length, + conn->std_strings); +} + +unsigned char * +PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length) +{ + return PQescapeByteaInternal(NULL, from, from_length, to_length, + static_std_strings); +} + + #define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3') #define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7') #define OCTVAL(CH) ((CH) - '0') @@ -2358,7 +2472,7 @@ PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen) * of a bytea, strtext, into binary, filling a buffer. It returns a * pointer to the buffer (or NULL on error), and the size of the * buffer in retbuflen. The pointer may subsequently be used as an - * argument to the function free(3). It is the reverse of PQescapeBytea. + * argument to the function PQfreemem. * * The following transformations are made: * \\ == ASCII 92 == \ diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index bafe0ddd6c9..44286628cda 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-fe.h,v 1.100 2003/08/27 00:33:34 petere Exp $ + * $Id: libpq-fe.h,v 1.100.2.1 2006/05/21 20:20:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -393,11 +393,18 @@ extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); /* Quoting strings before inclusion in queries. */ -extern size_t PQescapeString(char *to, const char *from, size_t length); -extern unsigned char *PQescapeBytea(const unsigned char *bintext, size_t binlen, - size_t *bytealen); +extern size_t PQescapeStringConn(PGconn *conn, + char *to, const char *from, size_t length, + int *error); +extern unsigned char *PQescapeByteaConn(PGconn *conn, + const unsigned char *from, size_t from_length, + size_t *to_length); extern unsigned char *PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen); +/* These forms are deprecated! */ +extern size_t PQescapeString(char *to, const char *from, size_t length); +extern unsigned char *PQescapeBytea(const unsigned char *from, size_t from_length, + size_t *to_length); diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 27a0d71ab75..bfcf4a9977f 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-int.h,v 1.82.2.1 2004/03/05 01:54:13 tgl Exp $ + * $Id: libpq-int.h,v 1.82.2.2 2006/05/21 20:20:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -293,6 +293,7 @@ struct pg_conn char cryptSalt[2]; /* password salt received from backend */ pgParameterStatus *pstatus; /* ParameterStatus data */ int client_encoding; /* encoding id */ + bool std_strings; /* standard_conforming_strings */ PGVerbosity verbosity; /* error/notice message verbosity */ PGlobjfuncs *lobjfuncs; /* private state for large-object access * fns */ diff --git a/src/interfaces/libpq/libpqdll.def b/src/interfaces/libpq/libpqdll.def index 4973ddd7138..595d6317b13 100644 --- a/src/interfaces/libpq/libpqdll.def +++ b/src/interfaces/libpq/libpqdll.def @@ -113,3 +113,5 @@ EXPORTS PQfformat @ 109 PQexecPrepared @ 110 PQsendQueryPrepared @ 111 + PQescapeStringConn @ 126 + PQescapeByteaConn @ 127 |