📄 xml.c
字号:
Datumxmltotext(PG_FUNCTION_ARGS){ xmltype *data = PG_GETARG_XML_P(0); /* It's actually binary compatible. */ PG_RETURN_TEXT_P((text *) data);}text *xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg){ if (xmloption_arg == XMLOPTION_DOCUMENT && !xml_is_document(data)) ereport(ERROR, (errcode(ERRCODE_NOT_AN_XML_DOCUMENT), errmsg("not an XML document"))); /* It's actually binary compatible, save for the above check. */ return (text *) data;}xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext){#ifdef USE_LIBXML XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr; xmltype *result; List *named_arg_strings; List *arg_strings; int i; ListCell *arg; ListCell *narg; xmlBufferPtr buf; xmlTextWriterPtr writer; /* * We first evaluate all the arguments, then start up libxml and create * the result. This avoids issues if one of the arguments involves a call * to some other function or subsystem that wants to use libxml on its own * terms. */ named_arg_strings = NIL; i = 0; foreach(arg, xmlExpr->named_args) { ExprState *e = (ExprState *) lfirst(arg); Datum value; bool isnull; char *str; value = ExecEvalExpr(e, econtext, &isnull, NULL); if (isnull) str = NULL; else str = OutputFunctionCall(&xmlExpr->named_outfuncs[i], value); named_arg_strings = lappend(named_arg_strings, str); i++; } arg_strings = NIL; foreach(arg, xmlExpr->args) { ExprState *e = (ExprState *) lfirst(arg); Datum value; bool isnull; char *str; value = ExecEvalExpr(e, econtext, &isnull, NULL); /* here we can just forget NULL elements immediately */ if (!isnull) { str = map_sql_value_to_xml_value(value, exprType((Node *) e->expr)); arg_strings = lappend(arg_strings, str); } } /* now safe to run libxml */ xml_init(); buf = xmlBufferCreate(); writer = xmlNewTextWriterMemory(buf, 0); xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name); forboth(arg, named_arg_strings, narg, xexpr->arg_names) { char *str = (char *) lfirst(arg); char *argname = strVal(lfirst(narg)); if (str) { xmlTextWriterWriteAttribute(writer, (xmlChar *) argname, (xmlChar *) str); pfree(str); } } foreach(arg, arg_strings) { char *str = (char *) lfirst(arg); xmlTextWriterWriteRaw(writer, (xmlChar *) str); } xmlTextWriterEndElement(writer); xmlFreeTextWriter(writer); result = xmlBuffer_to_xmltype(buf); xmlBufferFree(buf); return result;#else NO_XML_SUPPORT(); return NULL;#endif}xmltype *xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace){#ifdef USE_LIBXML xmlDocPtr doc; doc = xml_parse(data, xmloption_arg, preserve_whitespace, NULL); xmlFreeDoc(doc); return (xmltype *) data;#else NO_XML_SUPPORT(); return NULL;#endif}xmltype *xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null){#ifdef USE_LIBXML xmltype *result; StringInfoData buf; if (pg_strcasecmp(target, "xml") == 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), /* really */ errmsg("invalid XML processing instruction"), errdetail("XML processing instruction target name cannot be \"%s\".", target))); /* * Following the SQL standard, the null check comes after the syntax check * above. */ *result_is_null = arg_is_null; if (*result_is_null) return NULL; initStringInfo(&buf); appendStringInfo(&buf, "<?%s", target); if (arg != NULL) { char *string; string = _textout(arg); if (strstr(string, "?>") != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION), errmsg("invalid XML processing instruction"), errdetail("XML processing instruction cannot contain \"?>\"."))); appendStringInfoChar(&buf, ' '); appendStringInfoString(&buf, string + strspn(string, " ")); pfree(string); } appendStringInfoString(&buf, "?>"); result = stringinfo_to_xmltype(&buf); pfree(buf.data); return result;#else NO_XML_SUPPORT(); return NULL;#endif}xmltype *xmlroot(xmltype *data, text *version, int standalone){#ifdef USE_LIBXML char *str; size_t len; xmlChar *orig_version; int orig_standalone; StringInfoData buf; len = VARSIZE(data) - VARHDRSZ; str = palloc(len + 1); memcpy(str, VARDATA(data), len); str[len] = '\0'; parse_xml_decl((xmlChar *) str, &len, &orig_version, NULL, &orig_standalone); if (version) orig_version = xml_text2xmlChar(version); else orig_version = NULL; switch (standalone) { case XML_STANDALONE_YES: orig_standalone = 1; break; case XML_STANDALONE_NO: orig_standalone = 0; break; case XML_STANDALONE_NO_VALUE: orig_standalone = -1; break; case XML_STANDALONE_OMITTED: /* leave original value */ break; } initStringInfo(&buf); print_xml_decl(&buf, orig_version, 0, orig_standalone); appendStringInfoString(&buf, str + len); return stringinfo_to_xmltype(&buf);#else NO_XML_SUPPORT(); return NULL;#endif}/* * Validate document (given as string) against DTD (given as external link) * * This has been removed because it is a security hole: unprivileged users * should not be able to use Postgres to fetch arbitrary external files, * which unfortunately is exactly what libxml is willing to do with the DTD * parameter. */Datumxmlvalidate(PG_FUNCTION_ARGS){ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("xmlvalidate is not implemented"))); return 0;}boolxml_is_document(xmltype *arg){#ifdef USE_LIBXML bool result; xmlDocPtr doc = NULL; MemoryContext ccxt = CurrentMemoryContext; PG_TRY(); { doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true, NULL); result = true; } PG_CATCH(); { ErrorData *errdata; MemoryContext ecxt; ecxt = MemoryContextSwitchTo(ccxt); errdata = CopyErrorData(); if (errdata->sqlerrcode == ERRCODE_INVALID_XML_DOCUMENT) { FlushErrorState(); result = false; } else { MemoryContextSwitchTo(ecxt); PG_RE_THROW(); } } PG_END_TRY(); if (doc) xmlFreeDoc(doc); return result;#else /* not USE_LIBXML */ NO_XML_SUPPORT(); return false;#endif /* not USE_LIBXML */}/* * xml cleanup function for transaction end. This is also called on * subtransaction abort; see notes at top of file for rationale. */voidAtEOXact_xml(void){#ifdef USE_LIBXML xml_memory_cleanup();#endif}#ifdef USE_LIBXML/* * Set up for use of libxml --- this should be called by each function that * is about to use libxml facilities. * * TODO: xmlChar is utf8-char, make proper tuning (initdb with enc!=utf8 and * check) */static voidxml_init(void){ static bool first_time = true; if (first_time) { /* Stuff we need do only once per session */ MemoryContext oldcontext; /* * Currently, we have no pure UTF-8 support for internals -- check if * we can work. */ if (sizeof(char) != sizeof(xmlChar)) ereport(ERROR, (errmsg("could not initialize XML library"), errdetail("libxml2 has incompatible char type: sizeof(char)=%u, sizeof(xmlChar)=%u.", (int) sizeof(char), (int) sizeof(xmlChar)))); /* create error buffer in permanent context */ oldcontext = MemoryContextSwitchTo(TopMemoryContext); xml_err_buf = makeStringInfo(); MemoryContextSwitchTo(oldcontext); /* Now that xml_err_buf exists, safe to call xml_errorHandler */ xmlSetGenericErrorFunc(NULL, xml_errorHandler); /* Set up memory allocation our way, too */ xml_memory_init(); /* Check library compatibility */ LIBXML_TEST_VERSION; first_time = false; } else { /* Reset pre-existing buffer to empty */ Assert(xml_err_buf != NULL); resetStringInfo(xml_err_buf); /* * We re-establish the callback functions every time. This makes it * safe for other subsystems (PL/Perl, say) to also use libxml with * their own callbacks ... so long as they likewise set up the * callbacks on every use. It's cheap enough to not be worth worrying * about, anyway. */ xmlSetGenericErrorFunc(NULL, xml_errorHandler); xml_memory_init(); }}/* * SQL/XML allows storing "XML documents" or "XML content". "XML * documents" are specified by the XML specification and are parsed * easily by libxml. "XML content" is specified by SQL/XML as the * production "XMLDecl? content". But libxml can only parse the * "content" part, so we have to parse the XML declaration ourselves * to complete this. */#define CHECK_XML_SPACE(p) \ do { \ if (!xmlIsBlank_ch(*(p))) \ return XML_ERR_SPACE_REQUIRED; \ } while (0)#define SKIP_XML_SPACE(p) \ while (xmlIsBlank_ch(*(p))) (p)++/* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender *//* Beware of multiple evaluations of argument! */#define PG_XMLISNAMECHAR(c) \ (xmlIsBaseChar_ch(c) || xmlIsIdeographicQ(c) \ || xmlIsDigit_ch(c) \ || c == '.' || c == '-' || c == '_' || c == ':' \ || xmlIsCombiningQ(c) \ || xmlIsExtender_ch(c))static intparse_xml_decl(const xmlChar * str, size_t *lenp, xmlChar ** version, xmlChar ** encoding, int *standalone){ const xmlChar *p; const xmlChar *save_p; size_t len; int utf8char; int utf8len; xml_init(); if (version) *version = NULL; if (encoding) *encoding = NULL; if (standalone) *standalone = -1; p = str; if (xmlStrncmp(p, (xmlChar *) "<?xml", 5) != 0) goto finished; /* if next char is name char, it's a PI like <?xml-stylesheet ...?> */ utf8len = strlen((const char *) (p + 5)); utf8char = xmlGetUTF8Char(p + 5, &utf8len); if (PG_XMLISNAMECHAR(utf8char)) goto finished; p += 5; /* version */ CHECK_XML_SPACE(p); SKIP_XML_SPACE(p); if (xmlStrncmp(p, (xmlChar *) "version", 7) != 0) return XML_ERR_VERSION_MISSING; p += 7; SKIP_XML_SPACE(p); if (*p != '=') return XML_ERR_VERSION_MISSING; p += 1; SKIP_XML_SPACE(p); if (*p == '\'' || *p == '"') { const xmlChar *q; q = xmlStrchr(p + 1, *p); if (!q) return XML_ERR_VERSION_MISSING; if (version) *version = xmlStrndup(p + 1, q - p - 1); p = q + 1; } else return XML_ERR_VERSION_MISSING; /* encoding */ save_p = p; SKIP_XML_SPACE(p); if (xmlStrncmp(p, (xmlChar *) "encoding", 8) == 0) { CHECK_XML_SPACE(save_p); p += 8; SKIP_XML_SPACE(p); if (*p != '=') return XML_ERR_MISSING_ENCODING; p += 1; SKIP_XML_SPACE(p); if (*p == '\'' || *p == '"') { const xmlChar *q; q = xmlStrchr(p + 1, *p); if (!q) return XML_ERR_MISSING_ENCODING; if (encoding) *encoding = xmlStrndup(p + 1, q - p - 1); p = q + 1; } else return XML_ERR_MISSING_ENCODING; } else { p = save_p; } /* standalone */ save_p = p; SKIP_XML_SPACE(p); if (xmlStrncmp(p, (xmlChar *) "standalone", 10) == 0) { CHECK_XML_SPACE(save_p); p += 10; SKIP_XML_SPACE(p); if (*p != '=') return XML_ERR_STANDALONE_VALUE; p += 1; SKIP_XML_SPACE(p); if (xmlStrncmp(p, (xmlChar *) "'yes'", 5) == 0 || xmlStrncmp(p, (xmlChar *) "\"yes\"", 5) == 0) { *standalone = 1; p += 5; } else if (xmlStrncmp(p, (xmlChar *) "'no'", 4) == 0 || xmlStrncmp(p, (xmlChar *) "\"no\"", 4) == 0) { *standalone = 0; p += 4; } else return XML_ERR_STANDALONE_VALUE; } else { p = save_p; } SKIP_XML_SPACE(p); if (xmlStrncmp(p, (xmlChar *) "?>", 2) != 0) return XML_ERR_XMLDECL_NOT_FINISHED; p += 2;finished: len = p - str; for (p = str; p < str + len; p++) if (*p > 127) return XML_ERR_INVALID_CHAR; if (lenp) *lenp = len;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -