1 /*-------------------------------------------------------------------------
4 * XML data type support.
7 * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
10 * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.10 2007/01/05 22:19:42 momjian Exp $
12 *-------------------------------------------------------------------------
16 * Generally, XML type support is only available when libxml use was
17 * configured during the build. But even if that is not done, the
18 * type and all the functions are available, but most of them will
19 * fail. For one thing, this avoids having to manage variant catalog
20 * installations. But it also has nice effects such as that you can
21 * dump a database containing XML type data even if the server is not
22 * linked with libxml. Thus, make sure xml_out() works even if nothing
29 #include <libxml/chvalid.h>
30 #include <libxml/parser.h>
31 #include <libxml/tree.h>
32 #include <libxml/uri.h>
33 #include <libxml/xmlerror.h>
34 #endif /* USE_LIBXML */
37 #include "libpq/pqformat.h"
38 #include "mb/pg_wchar.h"
39 #include "nodes/execnodes.h"
40 #include "utils/builtins.h"
41 #include "utils/memutils.h"
42 #include "utils/xml.h"
47 #define PG_XML_DEFAULT_URI "dummy.xml"
49 static StringInfo xml_err_buf = NULL;
51 static void xml_init(void);
52 static void *xml_palloc(size_t size);
53 static void *xml_repalloc(void *ptr, size_t size);
54 static void xml_pfree(void *ptr);
55 static char *xml_pstrdup(const char *string);
56 static void xml_ereport(int level, int sqlcode,
57 const char *msg, void *ctxt);
58 static void xml_errorHandler(void *ctxt, const char *msg, ...);
59 static void xml_ereport_by_code(int level, int sqlcode,
60 const char *msg, int errcode);
61 static xmlChar *xml_text2xmlChar(text *in);
62 static xmlDocPtr xml_parse(text *data, bool is_document, bool preserve_whitespace);
64 #endif /* USE_LIBXML */
66 #define NO_XML_SUPPORT() \
68 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
69 errmsg("no XML support in this installation")))
73 xml_in(PG_FUNCTION_ARGS)
76 char *s = PG_GETARG_CSTRING(0);
81 vardata = palloc(len + VARHDRSZ);
82 VARATT_SIZEP(vardata) = len + VARHDRSZ;
83 memcpy(VARDATA(vardata), s, len);
86 * Parse the data to check if it is well-formed XML data. Assume
87 * that ERROR occurred if parsing failed.
89 xml_parse(vardata, false, true);
91 PG_RETURN_XML_P(vardata);
100 xml_out(PG_FUNCTION_ARGS)
102 xmltype *s = PG_GETARG_XML_P(0);
106 len = VARSIZE(s) - VARHDRSZ;
107 result = palloc(len + 1);
108 memcpy(result, VARDATA(s), len);
111 PG_RETURN_CSTRING(result);
116 xml_recv(PG_FUNCTION_ARGS)
119 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
124 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
126 result = (xmltype *) palloc(nbytes + VARHDRSZ);
127 VARATT_SIZEP(result) = nbytes + VARHDRSZ;
128 memcpy(VARDATA(result), str, nbytes);
132 * Parse the data to check if it is well-formed XML data. Assume
133 * that ERROR occurred if parsing failed.
135 xml_parse(result, false, true);
137 PG_RETURN_XML_P(result);
146 xml_send(PG_FUNCTION_ARGS)
148 xmltype *x = PG_GETARG_XML_P(0);
151 pq_begintypsend(&buf);
152 pq_sendbytes(&buf, VARDATA(x), VARSIZE(x) - VARHDRSZ);
153 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
159 appendStringInfoText(StringInfo str, const text *t)
161 appendBinaryStringInfo(str, VARDATA(t), VARSIZE(t) - VARHDRSZ);
166 stringinfo_to_xmltype(StringInfo buf)
171 len = buf->len + VARHDRSZ;
172 result = palloc(len);
173 VARATT_SIZEP(result) = len;
174 memcpy(VARDATA(result), buf->data, buf->len);
182 xmlcomment(PG_FUNCTION_ARGS)
185 text *arg = PG_GETARG_TEXT_P(0);
186 int len = VARATT_SIZEP(arg) - VARHDRSZ;
190 /* check for "--" in string or "-" at the end */
191 for (i = 1; i < len; i++)
192 if ((VARDATA(arg)[i] == '-' && VARDATA(arg)[i - 1] == '-')
193 || (VARDATA(arg)[i] == '-' && i == len - 1))
195 (errcode(ERRCODE_INVALID_XML_COMMENT),
196 errmsg("invalid XML comment")));
198 initStringInfo(&buf);
199 appendStringInfo(&buf, "<!--");
200 appendStringInfoText(&buf, arg);
201 appendStringInfo(&buf, "-->");
203 PG_RETURN_XML_P(stringinfo_to_xmltype(&buf));
212 texttoxml(PG_FUNCTION_ARGS)
214 text *data = PG_GETARG_TEXT_P(0);
216 PG_RETURN_XML_P(xmlparse(data, false, true));
221 xmlparse(text *data, bool is_document, bool preserve_whitespace)
224 xml_parse(data, is_document, preserve_whitespace);
226 return (xmltype *) data;
235 xmlpi(char *target, text *arg)
241 if (pg_strncasecmp(target, "xml", 3) == 0)
243 (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
244 errmsg("invalid XML processing instruction"),
245 errdetail("XML processing instruction target name cannot start with \"xml\".")));
247 initStringInfo(&buf);
249 appendStringInfo(&buf, "<?%s", target);
255 string = DatumGetCString(DirectFunctionCall1(textout,
256 PointerGetDatum(arg)));
257 if (strstr(string, "?>") != NULL)
259 (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
260 errmsg("invalid XML processing instruction"),
261 errdetail("XML processing instruction cannot contain \"?>\".")));
263 appendStringInfoChar(&buf, ' ');
264 appendStringInfoString(&buf, string);
267 appendStringInfoString(&buf, "?>");
269 result = stringinfo_to_xmltype(&buf);
280 xmlroot(xmltype *data, text *version, int standalone)
286 initStringInfo(&buf);
289 * FIXME: This is probably supposed to be cleverer if there
290 * already is an XML preamble.
292 appendStringInfo(&buf,"<?xml");
295 appendStringInfo(&buf, " version=\"");
296 appendStringInfoText(&buf, version);
297 appendStringInfo(&buf, "\"");
300 appendStringInfo(&buf, " standalone=\"%s\"",
301 (standalone == 1 ? "yes" : "no"));
302 appendStringInfo(&buf, "?>");
303 appendStringInfoText(&buf, (text *) data);
305 result = stringinfo_to_xmltype(&buf);
316 * Validate document (given as string) against DTD (given as external link)
317 * TODO !!! use text instead of cstring for second arg
318 * TODO allow passing DTD as a string value (not only as an URI)
319 * TODO redesign (see comment with '!!!' below)
322 xmlvalidate(PG_FUNCTION_ARGS)
325 text *data = PG_GETARG_TEXT_P(0);
326 text *dtdOrUri = PG_GETARG_TEXT_P(1);
328 xmlParserCtxtPtr ctxt = NULL;
329 xmlDocPtr doc = NULL;
330 xmlDtdPtr dtd = NULL;
334 /* We use a PG_TRY block to ensure libxml is cleaned up on error */
337 ctxt = xmlNewParserCtxt();
339 xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
340 "could not allocate parser context", ctxt);
342 doc = xmlCtxtReadMemory(ctxt, (char *) VARDATA(data),
343 VARSIZE(data) - VARHDRSZ,
344 PG_XML_DEFAULT_URI, NULL, 0);
346 xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
347 "could not parse XML data", ctxt);
350 uri = xmlCreateURI();
351 elog(NOTICE, "dtd - %s", dtdOrUri);
352 dtd = palloc(sizeof(xmlDtdPtr));
353 uri = xmlParseURI(dtdOrUri);
355 xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
356 "not implemented yet... (TODO)", ctxt);
359 dtd = xmlParseDTD(NULL, xml_text2xmlChar(dtdOrUri));
362 xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
363 "could not load DTD", ctxt);
365 if (xmlValidateDtd(xmlNewValidCtxt(), doc, dtd) == 1)
369 xml_ereport(NOTICE, ERRCODE_INVALID_XML_DOCUMENT,
370 "validation against DTD failed", ctxt);
381 xmlFreeParserCtxt(ctxt);
395 xmlFreeParserCtxt(ctxt);
402 PG_RETURN_BOOL(result);
403 #else /* not USE_LIBXML */
406 #endif /* not USE_LIBXML */
413 * Container for some init stuff (not good design!)
414 * TODO xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and check)
420 * Currently, we have no pure UTF-8 support for internals -- check
423 if (sizeof (char) != sizeof (xmlChar))
425 (errmsg("could not initialize XML library"),
426 errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.",
427 (int) sizeof(char), (int) sizeof(xmlChar))));
429 if (xml_err_buf == NULL)
431 /* First time through: create error buffer in permanent context */
432 MemoryContext oldcontext;
434 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
435 xml_err_buf = makeStringInfo();
436 MemoryContextSwitchTo(oldcontext);
440 /* Reset pre-existing buffer to empty */
441 xml_err_buf->data[0] = '\0';
442 xml_err_buf->len = 0;
444 /* Now that xml_err_buf exists, safe to call xml_errorHandler */
445 xmlSetGenericErrorFunc(NULL, xml_errorHandler);
447 xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
454 * Convert a C string to XML internal representation
456 * TODO maybe, libxml2's xmlreader is better? (do not construct DOM, yet do not use SAX - see xml_reader.c)
457 * TODO what about internal URI for docs? (see PG_XML_DEFAULT_URI below)
460 xml_parse(text *data, bool is_document, bool preserve_whitespace)
465 xmlParserCtxtPtr ctxt = NULL;
466 xmlDocPtr doc = NULL;
468 len = VARSIZE(data) - VARHDRSZ; /* will be useful later */
469 string = xml_text2xmlChar(data);
473 /* We use a PG_TRY block to ensure libxml is cleaned up on error */
476 ctxt = xmlNewParserCtxt();
478 xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
479 "could not allocate parser context", ctxt);
484 * Note, that here we try to apply DTD defaults
485 * (XML_PARSE_DTDATTR) according to SQL/XML:10.16.7.d:
486 * 'Default valies defined by internal DTD are applied'.
487 * As for external DTDs, we try to support them too, (see
490 doc = xmlCtxtReadMemory(ctxt, (char *) string, len,
491 PG_XML_DEFAULT_URI, NULL,
492 XML_PARSE_NOENT | XML_PARSE_DTDATTR
493 | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS));
495 xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT,
496 "invalid XML document", ctxt);
500 doc = xmlNewDoc(NULL);
503 * FIXME: An XMLDecl is supposed to be accepted before the
504 * content, but libxml doesn't allow this. Parse that
508 /* TODO resolve: xmlParseBalancedChunkMemory assumes that string is UTF8 encoded! */
509 res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, string, NULL);
511 xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT,
512 "invalid XML content", res_code);
515 /* TODO encoding issues
518 * - XML data has explicit encoding attribute in its prolog
519 * - if not, assume that enc. of XML data is the same as client's one
521 * The common rule is to accept the XML data only if its encoding
522 * is the same as encoding of the storage (server's). The other possible
523 * option is to accept all the docs, but DO TRANSFORMATION and, if needed,
526 * I think I'd stick the first way (for the 1st version),
527 * it's much simplier (less errors...)
534 xmlFreeParserCtxt(ctxt);
542 xmlFreeParserCtxt(ctxt);
554 * xmlChar<->text convertions
557 xml_text2xmlChar(text *in)
559 int32 len = VARSIZE(in) - VARHDRSZ;
562 res = palloc(len + 1);
563 memcpy(res, VARDATA(in), len);
571 * Wrappers for memory management functions
574 xml_palloc(size_t size)
581 xml_repalloc(void *ptr, size_t size)
583 return repalloc(ptr, size);
595 xml_pstrdup(const char *string)
597 return pstrdup(string);
602 * Wrapper for "ereport" function.
603 * Adds detail - libxml's native error message, if any.
606 xml_ereport(int level, int sqlcode,
607 const char *msg, void *ctxt)
609 xmlErrorPtr libxmlErr = NULL;
611 if (xml_err_buf->len > 0)
614 (errmsg("%s", xml_err_buf->data)));
615 xml_err_buf->data[0] = '\0';
616 xml_err_buf->len = 0;
620 libxmlErr = xmlCtxtGetLastError(ctxt);
622 if (libxmlErr == NULL)
630 /* as usual, libxml error message contains '\n'; get rid of it */
634 xmlErrDetail = pstrdup(libxmlErr->message);
635 xmlErrLen = strlen(xmlErrDetail);
636 for (i = 0; i < xmlErrLen; i++)
638 if (xmlErrDetail[i] == '\n')
639 xmlErrDetail[i] = '.';
644 errdetail("%s", xmlErrDetail)));
650 * Error handler for libxml error messages
653 xml_errorHandler(void *ctxt, const char *msg,...)
655 /* Append the formatted text to xml_err_buf */
661 /* Try to format the data. */
663 success = appendStringInfoVA(xml_err_buf, msg, args);
669 /* Double the buffer size and try again. */
670 enlargeStringInfo(xml_err_buf, xml_err_buf->maxlen);
676 * Return error message by libxml error code
677 * TODO make them closer to recommendations from Postgres manual
680 xml_ereport_by_code(int level, int sqlcode,
681 const char *msg, int code)
685 if (xml_err_buf->len > 0)
688 (errmsg("%s", xml_err_buf->data)));
689 xml_err_buf->data[0] = '\0';
690 xml_err_buf->len = 0;
695 case XML_ERR_INTERNAL_ERROR:
696 det = "libxml internal error";
698 case XML_ERR_ENTITY_LOOP:
699 det = "Detected an entity reference loop";
701 case XML_ERR_ENTITY_NOT_STARTED:
702 det = "EntityValue: \" or ' expected";
704 case XML_ERR_ENTITY_NOT_FINISHED:
705 det = "EntityValue: \" or ' expected";
707 case XML_ERR_ATTRIBUTE_NOT_STARTED:
708 det = "AttValue: \" or ' expected";
710 case XML_ERR_LT_IN_ATTRIBUTE:
711 det = "Unescaped '<' not allowed in attributes values";
713 case XML_ERR_LITERAL_NOT_STARTED:
714 det = "SystemLiteral \" or ' expected";
716 case XML_ERR_LITERAL_NOT_FINISHED:
717 det = "Unfinished System or Public ID \" or ' expected";
719 case XML_ERR_MISPLACED_CDATA_END:
720 det = "Sequence ']]>' not allowed in content";
722 case XML_ERR_URI_REQUIRED:
723 det = "SYSTEM or PUBLIC, the URI is missing";
725 case XML_ERR_PUBID_REQUIRED:
726 det = "PUBLIC, the Public Identifier is missing";
728 case XML_ERR_HYPHEN_IN_COMMENT:
729 det = "Comment must not contain '--' (double-hyphen)";
731 case XML_ERR_PI_NOT_STARTED:
732 det = "xmlParsePI : no target name";
734 case XML_ERR_RESERVED_XML_NAME:
735 det = "Invalid PI name";
737 case XML_ERR_NOTATION_NOT_STARTED:
738 det = "NOTATION: Name expected here";
740 case XML_ERR_NOTATION_NOT_FINISHED:
741 det = "'>' required to close NOTATION declaration";
743 case XML_ERR_VALUE_REQUIRED:
744 det = "Entity value required";
746 case XML_ERR_URI_FRAGMENT:
747 det = "Fragment not allowed";
749 case XML_ERR_ATTLIST_NOT_STARTED:
750 det = "'(' required to start ATTLIST enumeration";
752 case XML_ERR_NMTOKEN_REQUIRED:
753 det = "NmToken expected in ATTLIST enumeration";
755 case XML_ERR_ATTLIST_NOT_FINISHED:
756 det = "')' required to finish ATTLIST enumeration";
758 case XML_ERR_MIXED_NOT_STARTED:
759 det = "MixedContentDecl : '|' or ')*' expected";
761 case XML_ERR_PCDATA_REQUIRED:
762 det = "MixedContentDecl : '#PCDATA' expected";
764 case XML_ERR_ELEMCONTENT_NOT_STARTED:
765 det = "ContentDecl : Name or '(' expected";
767 case XML_ERR_ELEMCONTENT_NOT_FINISHED:
768 det = "ContentDecl : ',' '|' or ')' expected";
770 case XML_ERR_PEREF_IN_INT_SUBSET:
771 det = "PEReference: forbidden within markup decl in internal subset";
773 case XML_ERR_GT_REQUIRED:
774 det = "Expected '>'";
776 case XML_ERR_CONDSEC_INVALID:
777 det = "XML conditional section '[' expected";
779 case XML_ERR_EXT_SUBSET_NOT_FINISHED:
780 det = "Content error in the external subset";
782 case XML_ERR_CONDSEC_INVALID_KEYWORD:
783 det = "conditional section INCLUDE or IGNORE keyword expected";
785 case XML_ERR_CONDSEC_NOT_FINISHED:
786 det = "XML conditional section not closed";
788 case XML_ERR_XMLDECL_NOT_STARTED:
789 det = "Text declaration '<?xml' required";
791 case XML_ERR_XMLDECL_NOT_FINISHED:
792 det = "parsing XML declaration: '?>' expected";
794 case XML_ERR_EXT_ENTITY_STANDALONE:
795 det = "external parsed entities cannot be standalone";
797 case XML_ERR_ENTITYREF_SEMICOL_MISSING:
798 det = "EntityRef: expecting ';'";
800 case XML_ERR_DOCTYPE_NOT_FINISHED:
801 det = "DOCTYPE improperly terminated";
803 case XML_ERR_LTSLASH_REQUIRED:
804 det = "EndTag: '</' not found";
806 case XML_ERR_EQUAL_REQUIRED:
807 det = "Expected '='";
809 case XML_ERR_STRING_NOT_CLOSED:
810 det = "String not closed expecting \" or '";
812 case XML_ERR_STRING_NOT_STARTED:
813 det = "String not started expecting ' or \"";
815 case XML_ERR_ENCODING_NAME:
816 det = "Invalid XML encoding name";
818 case XML_ERR_STANDALONE_VALUE:
819 det = "Standalone accepts only 'yes' or 'no'";
821 case XML_ERR_DOCUMENT_EMPTY:
822 det = "Document is empty";
824 case XML_ERR_DOCUMENT_END:
825 det = "Extra content at the end of the document";
827 case XML_ERR_NOT_WELL_BALANCED:
828 det = "Chunk is not well balanced";
830 case XML_ERR_EXTRA_CONTENT:
831 det = "Extra content at the end of well balanced chunk";
833 case XML_ERR_VERSION_MISSING:
834 det = "Malformed declaration expecting version";
836 /* more err codes... Please, keep the order! */
837 case XML_ERR_ATTRIBUTE_WITHOUT_VALUE: /* 41 */
838 det ="Attribute without value";
840 case XML_ERR_ATTRIBUTE_REDEFINED:
841 det ="Attribute defined more than once in the same element";
843 case XML_ERR_COMMENT_NOT_FINISHED: /* 45 */
844 det = "Comment is not finished";
846 case XML_ERR_NAME_REQUIRED: /* 68 */
847 det = "Element name not found";
849 case XML_ERR_TAG_NOT_FINISHED: /* 77 */
850 det = "Closing tag not found";
853 det = "Unrecognized libxml error code: %d";
860 errdetail(det, code)));
865 * Convert one char in the current server encoding to a Unicode codepoint.
868 sqlchar_to_unicode(char *s)
871 pg_wchar ret[2]; /* need space for trailing zero */
873 utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s,
875 GetDatabaseEncoding(),
878 pg_encoding_mb2wchar_with_len(PG_UTF8, utf8string, ret, pg_mblen(s));
885 is_valid_xml_namefirst(pg_wchar c)
887 /* (Letter | '_' | ':') */
888 return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
889 || c == '_' || c == ':');
894 is_valid_xml_namechar(pg_wchar c)
896 /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */
897 return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c)
899 || c == '.' || c == '-' || c == '_' || c == ':'
900 || xmlIsCombiningQ(c)
901 || xmlIsExtenderQ(c));
903 #endif /* USE_LIBXML */
907 * Map SQL identifier to XML name; see SQL/XML:2003 section 9.1.
910 map_sql_identifier_to_xml_name(char *ident, bool fully_escaped)
916 initStringInfo(&buf);
918 for (p = ident; *p; p += pg_mblen(p))
920 if (*p == ':' && (p == ident || fully_escaped))
921 appendStringInfo(&buf, "_x003A_");
922 else if (*p == '_' && *(p+1) == 'x')
923 appendStringInfo(&buf, "_x005F_");
924 else if (fully_escaped && p == ident &&
925 pg_strncasecmp(p, "xml", 3) == 0)
928 appendStringInfo(&buf, "_x0078_");
930 appendStringInfo(&buf, "_x0058_");
934 pg_wchar u = sqlchar_to_unicode(p);
937 ? !is_valid_xml_namefirst(u)
938 : !is_valid_xml_namechar(u))
939 appendStringInfo(&buf, "_x%04X_", (unsigned int) u);
941 appendBinaryStringInfo(&buf, p, pg_mblen(p));
946 #else /* not USE_LIBXML */
949 #endif /* not USE_LIBXML */
954 * Map a Unicode codepoint into the current server encoding.
957 unicode_to_sqlchar(pg_wchar c)
959 static unsigned char utf8string[4];
967 utf8string[0] = 0xC0 | ((c >> 6) & 0x1F);
968 utf8string[1] = 0x80 | (c & 0x3F);
970 else if (c <= 0xFFFF)
972 utf8string[0] = 0xE0 | ((c >> 12) & 0x0F);
973 utf8string[1] = 0x80 | ((c >> 6) & 0x3F);
974 utf8string[2] = 0x80 | (c & 0x3F);
978 utf8string[0] = 0xF0 | ((c >> 18) & 0x07);
979 utf8string[1] = 0x80 | ((c >> 12) & 0x3F);
980 utf8string[2] = 0x80 | ((c >> 6) & 0x3F);
981 utf8string[3] = 0x80 | (c & 0x3F);
984 return (char *) pg_do_encoding_conversion(utf8string,
985 pg_mblen((char *) utf8string),
987 GetDatabaseEncoding());
992 * Map XML name to SQL identifier; see SQL/XML:2003 section 9.17.
995 map_xml_name_to_sql_identifier(char *name)
1000 initStringInfo(&buf);
1002 for (p = name; *p; p += pg_mblen(p))
1004 if (*p == '_' && *(p+1) == 'x'
1005 && isxdigit((unsigned char) *(p+2))
1006 && isxdigit((unsigned char) *(p+3))
1007 && isxdigit((unsigned char) *(p+4))
1008 && isxdigit((unsigned char) *(p+5))
1013 sscanf(p + 2, "%X", &u);
1014 appendStringInfoString(&buf, unicode_to_sqlchar(u));
1018 appendBinaryStringInfo(&buf, p, pg_mblen(p));