/* * Output an attribute to text * This takes portions of the code of CopyAttributeOutText */ static void attribute_out_text(StringInfo buf, char *string) { char *ptr; char c; char *start; char delimc = COPYOPS_DELIMITER; bool need_transcoding, encoding_embeds_ascii; int file_encoding = pg_get_client_encoding(); need_transcoding = (file_encoding != GetDatabaseEncoding() || pg_database_encoding_max_length() > 1); encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(file_encoding); if (need_transcoding) ptr = pg_server_to_any(string, strlen(string), file_encoding); else ptr = string; /* * We have to grovel through the string searching for control characters * and instances of the delimiter character. In most cases, though, these * are infrequent. To avoid overhead from calling CopySendData once per * character, we dump out all characters between escaped characters in a * single call. The loop invariant is that the data from "start" to "ptr" * can be sent literally, but hasn't yet been. * * We can skip pg_encoding_mblen() overhead when encoding is safe, because * in valid backend encodings, extra bytes of a multibyte character never * look like ASCII. This loop is sufficiently performance-critical that * it's worth making two copies of it to get the IS_HIGHBIT_SET() test out * of the normal safe-encoding path. */ if (encoding_embeds_ascii) { start = ptr; while ((c = *ptr) != '\0') { if ((unsigned char) c < (unsigned char) 0x20) { /* * \r and \n must be escaped, the others are traditional. We * prefer to dump these using the C-like notation, rather than * a backslash and the literal character, because it makes the * dump file a bit more proof against Microsoftish data * mangling. */ switch (c) { case '\b': c = 'b'; break; case '\f': c = 'f'; break; case '\n': c = 'n'; break; case '\r': c = 'r'; break; case '\t': c = 't'; break; case '\v': c = 'v'; break; default: /* If it's the delimiter, must backslash it */ if (c == delimc) break; /* All ASCII control chars are length 1 */ ptr++; continue; /* fall to end of loop */ } /* if we get here, we need to convert the control char */ DUMPSOFAR(); appendStringInfoCharMacro(buf, '\\'); appendStringInfoCharMacro(buf, c); start = ++ptr; } else if (c == '\\' || c == delimc) { DUMPSOFAR(); appendStringInfoCharMacro(buf, '\\'); appendStringInfoCharMacro(buf, c); start = ++ptr; } else if (IS_HIGHBIT_SET(c)) ptr += pg_encoding_mblen(file_encoding, ptr); else ptr++; } } else { start = ptr; while ((c = *ptr) != '\0') { if ((unsigned char) c < (unsigned char) 0x20) { /* * \r and \n must be escaped, the others are traditional. We * prefer to dump these using the C-like notation, rather than * a backslash and the literal character, because it makes the * dump file a bit more proof against Microsoftish data * mangling. */ switch (c) { case '\b': c = 'b'; break; case '\f': c = 'f'; break; case '\n': c = 'n'; break; case '\r': c = 'r'; break; case '\t': c = 't'; break; case '\v': c = 'v'; break; default: /* If it's the delimiter, must backslash it */ if (c == delimc) break; /* All ASCII control chars are length 1 */ ptr++; continue; /* fall to end of loop */ } /* if we get here, we need to convert the control char */ DUMPSOFAR(); appendStringInfoCharMacro(buf, '\\'); appendStringInfoCharMacro(buf, c); start = ++ptr; } else if (c == '\\' || c == delimc) { DUMPSOFAR(); appendStringInfoCharMacro(buf, '\\'); appendStringInfoCharMacro(buf, c); start = ++ptr; } else ptr++; } } DUMPSOFAR(); }
void tf_write_attribute_out_text(StringInfo msgbuf, char delim, char *string) { char *ptr; char *start; char c; char delimc = delim; ptr = string; start = ptr; while ((c = *ptr) != '\0') { if ((unsigned char) c < (unsigned char) 0x20) { /* * \r and \n must be escaped, the others are traditional. We * prefer to dump these using the C-like notation, rather than * a backslash and the literal character, because it makes the * dump file a bit more proof against Microsoftish data * mangling. */ switch (c) { case '\b': c = 'b'; break; case '\f': c = 'f'; break; case '\n': c = 'n'; break; case '\r': c = 'r'; break; case '\t': c = 't'; break; case '\v': c = 'v'; break; default: /* If it's the delimiter, must backslash it */ if (c == delimc) break; /* All ASCII control chars are length 1 */ ptr++; continue; /* fall to end of loop */ } /* if we get here, we need to convert the control char */ DUMPSOFAR(); tf_write_char(msgbuf, '\\'); tf_write_char(msgbuf, c); start = ++ptr; /* do not include char in next run */ } else if (c == '\\' || c == delimc) { DUMPSOFAR(); tf_write_char(msgbuf, '\\'); start = ptr++; /* we include char in next run */ } else ptr++; } DUMPSOFAR(); }