📄 xml.c
字号:
return XML_ERR_OK;}/* * Write an XML declaration. On output, we adjust the XML declaration * as follows. (These rules are the moral equivalent of the clause * "Serialization of an XML value" in the SQL standard.) * * We try to avoid generating an XML declaration if possible. This is * so that you don't get trivial things like xml '<foo/>' resulting in * '<?xml version="1.0"?><foo/>', which would surely be annoying. We * must provide a declaration if the standalone property is specified * or if we include an encoding declaration. If we have a * declaration, we must specify a version (XML requires this). * Otherwise we only make a declaration if the version is not "1.0", * which is the default version specified in SQL:2003. */static boolprint_xml_decl(StringInfo buf, const xmlChar * version, pg_enc encoding, int standalone){ xml_init(); if ((version && strcmp((char *) version, PG_XML_DEFAULT_VERSION) != 0) || (encoding && encoding != PG_UTF8) || standalone != -1) { appendStringInfoString(buf, "<?xml"); if (version) appendStringInfo(buf, " version=\"%s\"", version); else appendStringInfo(buf, " version=\"%s\"", PG_XML_DEFAULT_VERSION); if (encoding && encoding != PG_UTF8) { /* * XXX might be useful to convert this to IANA names (ISO-8859-1 * instead of LATIN1 etc.); needs field experience */ appendStringInfo(buf, " encoding=\"%s\"", pg_encoding_to_char(encoding)); } if (standalone == 1) appendStringInfoString(buf, " standalone=\"yes\""); else if (standalone == 0) appendStringInfoString(buf, " standalone=\"no\""); appendStringInfoString(buf, "?>"); return true; } else return false;}/* * Convert a C string to XML internal representation * * TODO maybe, libxml2's xmlreader is better? (do not construct DOM, * yet do not use SAX - see xmlreader.c) */static xmlDocPtrxml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, xmlChar * encoding){ int32 len; xmlChar *string; xmlChar *utf8string; xmlParserCtxtPtr ctxt; xmlDocPtr doc; len = VARSIZE(data) - VARHDRSZ; /* will be useful later */ string = xml_text2xmlChar(data); utf8string = pg_do_encoding_conversion(string, len, encoding ? xmlChar_to_encoding(encoding) : GetDatabaseEncoding(), PG_UTF8); xml_init(); xmlInitParser(); ctxt = xmlNewParserCtxt(); if (ctxt == NULL) xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, "could not allocate parser context"); if (xmloption_arg == XMLOPTION_DOCUMENT) { /* * Note, that here we try to apply DTD defaults * (XML_PARSE_DTDATTR) according to SQL/XML:10.16.7.d: 'Default * values defined by internal DTD are applied'. As for external * DTDs, we try to support them too, (see SQL/XML:10.16.7.e) */ doc = xmlCtxtReadDoc(ctxt, utf8string, NULL, "UTF-8", XML_PARSE_NOENT | XML_PARSE_DTDATTR | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS)); if (doc == NULL) xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT, "invalid XML document"); } else { int res_code; size_t count; xmlChar *version = NULL; int standalone = -1; doc = xmlNewDoc(NULL); res_code = parse_xml_decl(utf8string, &count, &version, NULL, &standalone); if (res_code != 0) xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT, "invalid XML content: invalid XML declaration", res_code); res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, utf8string + count, NULL); if (res_code != 0) xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT, "invalid XML content"); doc->version = xmlStrdup(version); doc->encoding = xmlStrdup((xmlChar *) "UTF-8"); doc->standalone = standalone; } xmlFreeParserCtxt(ctxt); return doc;}/* * xmlChar<->text convertions */static xmlChar *xml_text2xmlChar(text *in){ int32 len = VARSIZE(in) - VARHDRSZ; xmlChar *res; res = palloc(len + 1); memcpy(res, VARDATA(in), len); res[len] = '\0'; return (res);}/* * Manage the special context used for all libxml allocations */static voidxml_memory_init(void){ /* * Create memory context if not there already. We make it a child of * TopMemoryContext, even though our current policy is that it doesn't * survive past transaction end, because we want to be really really * sure it doesn't go away before we've called xmlCleanupParser(). */ if (LibxmlContext == NULL) LibxmlContext = AllocSetContextCreate(TopMemoryContext, "LibxmlContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* Re-establish the callbacks even if already set */ xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);}static voidxml_memory_cleanup(void){ if (LibxmlContext != NULL) { /* Give libxml a chance to clean up dangling pointers */ xmlCleanupParser(); /* And flush the context */ MemoryContextDelete(LibxmlContext); LibxmlContext = NULL; }}/* * Wrappers for memory management functions */static void *xml_palloc(size_t size){ return MemoryContextAlloc(LibxmlContext, size);}static void *xml_repalloc(void *ptr, size_t size){ return repalloc(ptr, size);}static voidxml_pfree(void *ptr){ pfree(ptr);}static char *xml_pstrdup(const char *string){ return MemoryContextStrdup(LibxmlContext, string);}/* * Wrapper for "ereport" function for XML-related errors. The "msg" * is the SQL-level message; some can be adopted from the SQL/XML * standard. This function adds libxml's native error messages, if * any, as detail. */static voidxml_ereport(int level, int sqlcode, const char *msg){ char *detail; if (xml_err_buf->len > 0) { detail = pstrdup(xml_err_buf->data); resetStringInfo(xml_err_buf); } else detail = NULL; /* libxml error messages end in '\n'; get rid of it */ if (detail) { size_t len; len = strlen(detail); if (len > 0 && detail[len - 1] == '\n') detail[len - 1] = '\0'; ereport(level, (errcode(sqlcode), errmsg("%s", msg), errdetail("%s", detail))); } else { ereport(level, (errcode(sqlcode), errmsg("%s", msg))); }}/* * Error handler for libxml error messages */static voidxml_errorHandler(void *ctxt, const char *msg,...){ /* Append the formatted text to xml_err_buf */ for (;;) { va_list args; bool success; /* Try to format the data. */ va_start(args, msg); success = appendStringInfoVA(xml_err_buf, msg, args); va_end(args); if (success) break; /* Double the buffer size and try again. */ enlargeStringInfo(xml_err_buf, xml_err_buf->maxlen); }}/* * Wrapper for "ereport" function for XML-related errors. The "msg" * is the SQL-level message; some can be adopted from the SQL/XML * standard. This function uses "code" to create a textual detail * message. At the moment, we only need to cover those codes that we * may raise in this file. */static voidxml_ereport_by_code(int level, int sqlcode, const char *msg, int code){ const char *det; switch (code) { case XML_ERR_INVALID_CHAR: det = gettext_noop("Invalid character value."); break; case XML_ERR_SPACE_REQUIRED: det = gettext_noop("Space required."); break; case XML_ERR_STANDALONE_VALUE: det = gettext_noop("standalone accepts only 'yes' or 'no'."); break; case XML_ERR_VERSION_MISSING: det = gettext_noop("Malformed declaration: missing version."); break; case XML_ERR_MISSING_ENCODING: det = gettext_noop("Missing encoding in text declaration."); break; case XML_ERR_XMLDECL_NOT_FINISHED: det = gettext_noop("Parsing XML declaration: '?>' expected."); break; default: det = gettext_noop("Unrecognized libxml error code: %d."); break; } ereport(level, (errcode(sqlcode), errmsg("%s", msg), errdetail(det, code)));}/* * Convert one char in the current server encoding to a Unicode codepoint. */static pg_wcharsqlchar_to_unicode(char *s){ char *utf8string; pg_wchar ret[2]; /* need space for trailing zero */ utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s, pg_mblen(s), GetDatabaseEncoding(), PG_UTF8); pg_encoding_mb2wchar_with_len(PG_UTF8, utf8string, ret, pg_mblen(s)); return ret[0];}static boolis_valid_xml_namefirst(pg_wchar c){ /* (Letter | '_' | ':') */ return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c) || c == '_' || c == ':');}static boolis_valid_xml_namechar(pg_wchar c){ /* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */ return (xmlIsBaseCharQ(c) || xmlIsIdeographicQ(c) || xmlIsDigitQ(c) || c == '.' || c == '-' || c == '_' || c == ':' || xmlIsCombiningQ(c) || xmlIsExtenderQ(c));}#endif /* USE_LIBXML *//* * Map SQL identifier to XML name; see SQL/XML:2003 section 9.1. */char *map_sql_identifier_to_xml_name(char *ident, bool fully_escaped, bool escape_period){#ifdef USE_LIBXML StringInfoData buf; char *p; /* * SQL/XML doesn't make use of this case anywhere, so it's probably a * mistake. */ Assert(fully_escaped || !escape_period); initStringInfo(&buf); for (p = ident; *p; p += pg_mblen(p)) { if (*p == ':' && (p == ident || fully_escaped)) appendStringInfo(&buf, "_x003A_"); else if (*p == '_' && *(p + 1) == 'x') appendStringInfo(&buf, "_x005F_"); else if (fully_escaped && p == ident && pg_strncasecmp(p, "xml", 3) == 0) { if (*p == 'x') appendStringInfo(&buf, "_x0078_"); else appendStringInfo(&buf, "_x0058_"); } else if (escape_period && *p == '.') appendStringInfo(&buf, "_x002E_"); else { pg_wchar u = sqlchar_to_unicode(p); if ((p == ident) ? !is_valid_xml_namefirst(u) : !is_valid_xml_namechar(u)) appendStringInfo(&buf, "_x%04X_", (unsigned int) u); else appendBinaryStringInfo(&buf, p, pg_mblen(p)); } } return buf.data;#else /* not USE_LIBXML */ NO_XML_SUPPORT(); return NULL;#endif /* not USE_LIBXML */}/* * Map a Unicode codepoint into the current server encoding. */static char *unicode_to_sqlchar(pg_wchar c){ static unsigned char utf8string[5]; /* need trailing zero */ if (c <= 0x7F) { utf8string[0] = c; } else if (c <= 0x7FF) { utf8string[0] = 0xC0 | ((c >> 6) & 0x1F); utf8string[1] = 0x80 | (c & 0x3F); } else if (c <= 0xFFFF) { utf8string[0] = 0xE0 | ((c >> 12) & 0x0F); utf8string[1] = 0x80 | ((c >> 6) & 0x3F); utf8string[2] = 0x80 | (c & 0x3F); } else { utf8string[0] = 0xF0 | ((c >> 18) & 0x07); utf8string[1] = 0x80 | ((c >> 12) & 0x3F); utf8string[2] = 0x80 | ((c >> 6) & 0x3F); utf8string[3] = 0x80 | (c & 0x3F); } return (char *) pg_do_encoding_conversion(utf8string, pg_mblen((char *) utf8string), PG_UTF8, GetDatabaseEncoding());}/* * Map XML name to SQL identifier; see SQL/XML:2003 section 9.17. */char *map_xml_name_to_sql_identifier(char *name){ StringInfoData buf; char *p; initStringInfo(&buf); for (p = name; *p; p += pg_mblen(p)) { if (*p == '_' && *(p + 1) == 'x' && isxdigit((unsigned char) *(p + 2)) && isxdigit((unsigned char) *(p + 3)) && isxdigit((unsigned char) *(p + 4)) && isxdigit((unsigned char) *(p + 5)) && *(p + 6) == '_') { unsigned int u; sscanf(p + 2, "%X", &u); appendStringInfoString(&buf, unicode_to_sqlchar(u)); p += 6; } else appendBinaryStringInfo(&buf, p, pg_mblen(p)); } return buf.data;}/* * Map SQL value to XML value; see SQL/XML:2003 section 9.16. */char *map_sql_value_to_xml_value(Datum value, Oid type){ StringInfoData buf; initStringInfo(&buf); if (type_is_array(type)) { ArrayType *array; Oid elmtype; int16 elmlen; bool elmbyval; char elmalign; int num_elems; Datum *elem_values; bool *elem_nulls; int i; array = DatumGetArrayTypeP(value); elmtype = ARR_ELEMTYPE(array); get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign); deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, &elem_values, &elem_nulls, &num_elems); for (i = 0; i < num_elems; i++) { if (elem_nulls[i]) continue; appendStringInfoString(&buf, "<element>"); appendStringInfoString(&buf, map_sql_value_to_xml_value(elem_values[i], elmtype)); appendStringInfoString(&buf, "</element>");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -