/* * Convert a string value to an SQL string literal and append it to * the given buffer. Encoding and string syntax rules are as indicated * by current settings of the PGconn. */ void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn) { size_t length = strlen(str); /* * XXX This is a kluge to silence escape_string_warning in our utility * programs. It should go away someday. */ if (strchr(str, '\\') != NULL && PQserverVersion(conn) >= 80100) { /* ensure we are not adjacent to an identifier */ if (buf->len > 0 && buf->data[buf->len - 1] != ' ') appendPQExpBufferChar(buf, ' '); appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX); appendStringLiteral(buf, str, PQclientEncoding(conn), false); return; } /* XXX end kluge */ if (!enlargePQExpBuffer(buf, 2 * length + 2)) return; appendPQExpBufferChar(buf, '\''); buf->len += PQescapeStringConn(conn, buf->data + buf->len, str, length, NULL); appendPQExpBufferChar(buf, '\''); }
/* * appendPQExpBufferChar * Append a single byte to str. * Like appendPQExpBuffer(str, "%c", ch) but much faster. */ void appendPQExpBufferChar(PQExpBuffer str, char ch) { /* Make more room if needed */ if (!enlargePQExpBuffer(str, 1)) return; /* OK, append the character */ str->data[str->len] = ch; str->len++; str->data[str->len] = '\0'; }
/* * appendBinaryPQExpBuffer * * Append arbitrary binary data to a PQExpBuffer, allocating more space * if necessary. */ void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen) { /* Make more room if needed */ if (!enlargePQExpBuffer(str, datalen)) return; /* OK, append the data */ memcpy(str->data + str->len, data, datalen); str->len += datalen; /* * Keep a trailing null in place, even though it's probably useless for * binary data... */ str->data[str->len] = '\0'; }
/* * Convert a bytea value (presented as raw bytes) to an SQL string literal * and append it to the given buffer. We assume the specified * standard_conforming_strings setting. * * This is needed in situations where we do not have a PGconn available. * Where we do, PQescapeByteaConn is a better choice. */ void appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length, bool std_strings) { const unsigned char *vp; unsigned char *rp; size_t i; size_t len; size_t bslash_len = (std_strings ? 1 : 2); len = 2; /* for the quote marks */ vp = str; for (i = length; i > 0; i--, vp++) { if (*vp < 0x20 || *vp > 0x7e) len += bslash_len + 3; else if (*vp == '\'') len += 2; else if (*vp == '\\') len += bslash_len + bslash_len; else len++; } if (!enlargePQExpBuffer(buf, len)) return; rp = (unsigned char *) (buf->data + buf->len); *rp++ = '\''; vp = str; for (i = length; i > 0; i--, vp++) { if (*vp < 0x20 || *vp > 0x7e) { int val = *vp; if (!std_strings) *rp++ = '\\'; *rp++ = '\\'; *rp++ = (val >> 6) + '0'; *rp++ = ((val >> 3) & 07) + '0'; *rp++ = (val & 07) + '0'; } else if (*vp == '\'')
/* * printfPQExpBuffer * Format text data under the control of fmt (an sprintf-like format string) * and insert it into str. More space is allocated to str if necessary. * This is a convenience routine that does the same thing as * resetPQExpBuffer() followed by appendPQExpBuffer(). */ void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...) { va_list args; size_t avail; int nprinted; resetPQExpBuffer(str); if (PQExpBufferBroken(str)) return; /* already failed */ for (;;) { /* * Try to format the given string into the available space; but if * there's hardly any space, don't bother trying, just fall through to * enlarge the buffer first. */ if (str->maxlen > str->len + 16) { avail = str->maxlen - str->len - 1; va_start(args, fmt); nprinted = vsnprintf(str->data + str->len, avail, fmt, args); va_end(args); /* * Note: some versions of vsnprintf return the number of chars * actually stored, but at least one returns -1 on failure. Be * conservative about believing whether the print worked. */ if (nprinted >= 0 && nprinted < (int) avail - 1) { /* Success. Note nprinted does not include trailing null. */ str->len += nprinted; break; } } /* Double the buffer size and try again. */ if (!enlargePQExpBuffer(str, str->maxlen)) return; /* oops, out of memory */ } }
/* * Convert a bytea value (presented as raw bytes) to an SQL string literal * and append it to the given buffer. We assume the specified * standard_conforming_strings setting. * * This is needed in situations where we do not have a PGconn available. * Where we do, PQescapeByteaConn is a better choice. */ void appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length, bool std_strings) { const unsigned char *source = str; char *target; static const char hextbl[] = "0123456789abcdef"; /* * This implementation is hard-wired to produce hex-format output. We do * not know the server version the output will be loaded into, so making * an intelligent format choice is impossible. It might be better to * always use the old escaped format. */ if (!enlargePQExpBuffer(buf, 2 * length + 5)) return; target = buf->data + buf->len; *target++ = '\''; if (!std_strings) *target++ = '\\'; *target++ = '\\'; *target++ = 'x'; while (length-- > 0) { unsigned char c = *source++; *target++ = hextbl[(c >> 4) & 0xF]; *target++ = hextbl[c & 0xF]; } /* Write the terminating quote and NUL character. */ *target++ = '\''; *target = '\0'; buf->len = target - buf->data; }
/* * unlike the server code, this function automaticcaly extend buffer. */ bool appendStringInfoVA_c(StringInfo str, const char *fmt, va_list args) { size_t avail; int nprinted; Assert(str != NULL); Assert(str->maxlen > 0); avail = str->maxlen - str->len - 1; nprinted = vsnprintf(str->data + str->len, avail, fmt, args); if (nprinted >= 0 && nprinted < (int) avail - 1) { str->len += nprinted; return true; } /* Double the buffer size and try again. */ enlargePQExpBuffer(str, str->maxlen); return false; }
/* * Convert a string value to an SQL string literal and append it to * the given buffer. We assume the specified client_encoding and * standard_conforming_strings settings. * * This is essentially equivalent to libpq's PQescapeStringInternal, * except for the output buffer structure. We need it in situations * where we do not have a PGconn available. Where we do, * appendStringLiteralConn is a better choice. */ void appendStringLiteral(PQExpBuffer buf, const char *str, int encoding, bool std_strings) { size_t length = strlen(str); const char *source = str; char *target; if (!enlargePQExpBuffer(buf, 2 * length + 2)) return; target = buf->data + buf->len; *target++ = '\''; while (*source != '\0') { char c = *source; int len; int i; /* Fast path for plain ASCII */ if (!IS_HIGHBIT_SET(c)) { /* Apply quoting if needed */ if (SQL_STR_DOUBLE(c, !std_strings)) *target++ = c; /* Copy the character */ *target++ = c; source++; continue; } /* Slow path for possible multibyte characters */ len = PQmblen(source, encoding); /* Copy the character */ for (i = 0; i < len; i++) { if (*source == '\0') break; *target++ = *source++; } /* * 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) { char *stop = buf->data + buf->maxlen - 2; for (; i < len; i++) { if (target >= stop) break; *target++ = ' '; } break; } } /* Write the terminating quote and NUL character. */ *target++ = '\''; *target = '\0'; buf->len = target - buf->data; }
/* * appendPQExpBufferVA * Shared guts of printfPQExpBuffer/appendPQExpBuffer. * Attempt to format data and append it to str. Returns true if done * (either successful or hard failure), false if need to retry. */ static bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) { size_t avail; size_t needed; int nprinted; /* * Try to format the given string into the available space; but if there's * hardly any space, don't bother trying, just enlarge the buffer first. */ if (str->maxlen > str->len + 16) { /* * Note: we intentionally leave one byte unused, as a guard against * old broken versions of vsnprintf. */ avail = str->maxlen - str->len - 1; errno = 0; nprinted = vsnprintf(str->data + str->len, avail, fmt, args); /* * If vsnprintf reports an error other than ENOMEM, fail. */ if (nprinted < 0 && errno != 0 && errno != ENOMEM) { markPQExpBufferBroken(str); return true; } /* * Note: some versions of vsnprintf return the number of chars * actually stored, not the total space needed as C99 specifies. And * at least one returns -1 on failure. Be conservative about * believing whether the print worked. */ if (nprinted >= 0 && (size_t) nprinted < avail - 1) { /* Success. Note nprinted does not include trailing null. */ str->len += nprinted; return true; } if (nprinted >= 0 && (size_t) nprinted > avail) { /* * This appears to be a C99-compliant vsnprintf, so believe its * estimate of the required space. (If it's wrong, the logic will * still work, but we may loop multiple times.) Note that the * space needed should be only nprinted+1 bytes, but we'd better * allocate one more than that so that the test above will succeed * next time. * * In the corner case where the required space just barely * overflows, fail. */ if (nprinted > INT_MAX - 2) { markPQExpBufferBroken(str); return true; } needed = nprinted + 2; } else { /* * Buffer overrun, and we don't know how much space is needed. * Estimate twice the previous buffer size, but not more than * INT_MAX. */ if (avail >= INT_MAX / 2) needed = INT_MAX; else needed = avail * 2; } } else { /* * We have to guess at how much to enlarge, since we're skipping the * formatting work. */ needed = 32; } /* Increase the buffer size and try again. */ if (!enlargePQExpBuffer(str, needed)) return true; /* oops, out of memory */ return false; }
/* * appendPQExpBufferVA * Shared guts of printfPQExpBuffer/appendPQExpBuffer. * Attempt to format data and append it to str. Returns true if done * (either successful or hard failure), false if need to retry. * * Caution: callers must be sure to preserve their entry-time errno * when looping, in case the fmt contains "%m". */ static bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) { size_t avail; size_t needed; int nprinted; /* * Try to format the given string into the available space; but if there's * hardly any space, don't bother trying, just enlarge the buffer first. */ if (str->maxlen > str->len + 16) { avail = str->maxlen - str->len; nprinted = vsnprintf(str->data + str->len, avail, fmt, args); /* * If vsnprintf reports an error, fail (we assume this means there's * something wrong with the format string). */ if (unlikely(nprinted < 0)) { markPQExpBufferBroken(str); return true; } if ((size_t) nprinted < avail) { /* Success. Note nprinted does not include trailing null. */ str->len += nprinted; return true; } /* * We assume a C99-compliant vsnprintf, so believe its estimate of the * required space, and add one for the trailing null. (If it's wrong, * the logic will still work, but we may loop multiple times.) * * Choke if the required space would exceed INT_MAX, since str->maxlen * can't represent more than that. */ if (unlikely(nprinted > INT_MAX - 1)) { markPQExpBufferBroken(str); return true; } needed = nprinted + 1; } else { /* * We have to guess at how much to enlarge, since we're skipping the * formatting work. Fortunately, because of enlargePQExpBuffer's * preference for power-of-2 sizes, this number isn't very sensitive; * the net effect is that we'll double the buffer size before trying * to run vsnprintf, which seems sensible. */ needed = 32; } /* Increase the buffer size and try again. */ if (!enlargePQExpBuffer(str, needed)) return true; /* oops, out of memory */ return false; }