mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
Back-patch changes of 2009-05-13 in xml.c's memory management.
I was afraid to do this when these changes were first made, but now that 8.4 has seen some field use it should be all right to back-patch. These changes are really quite necessary in order to give xml.c any hope of co-existing with loadable modules that also wish to use libxml2.
This commit is contained in:
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.257.2.6 2010/01/24 21:49:39 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.257.2.7 2010/03/01 02:21:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -46,7 +46,6 @@
|
|||||||
#include "utils/inval.h"
|
#include "utils/inval.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/relcache.h"
|
#include "utils/relcache.h"
|
||||||
#include "utils/xml.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1743,7 +1742,6 @@ CommitTransaction(void)
|
|||||||
|
|
||||||
AtEOXact_GUC(true, 1);
|
AtEOXact_GUC(true, 1);
|
||||||
AtEOXact_SPI(true);
|
AtEOXact_SPI(true);
|
||||||
AtEOXact_xml();
|
|
||||||
AtEOXact_on_commit_actions(true);
|
AtEOXact_on_commit_actions(true);
|
||||||
AtEOXact_Namespace(true);
|
AtEOXact_Namespace(true);
|
||||||
/* smgrcommit already done */
|
/* smgrcommit already done */
|
||||||
@ -1976,7 +1974,6 @@ PrepareTransaction(void)
|
|||||||
/* PREPARE acts the same as COMMIT as far as GUC is concerned */
|
/* PREPARE acts the same as COMMIT as far as GUC is concerned */
|
||||||
AtEOXact_GUC(true, 1);
|
AtEOXact_GUC(true, 1);
|
||||||
AtEOXact_SPI(true);
|
AtEOXact_SPI(true);
|
||||||
AtEOXact_xml();
|
|
||||||
AtEOXact_on_commit_actions(true);
|
AtEOXact_on_commit_actions(true);
|
||||||
AtEOXact_Namespace(true);
|
AtEOXact_Namespace(true);
|
||||||
/* smgrcommit already done */
|
/* smgrcommit already done */
|
||||||
@ -2122,7 +2119,6 @@ AbortTransaction(void)
|
|||||||
|
|
||||||
AtEOXact_GUC(false, 1);
|
AtEOXact_GUC(false, 1);
|
||||||
AtEOXact_SPI(false);
|
AtEOXact_SPI(false);
|
||||||
AtEOXact_xml();
|
|
||||||
AtEOXact_on_commit_actions(false);
|
AtEOXact_on_commit_actions(false);
|
||||||
AtEOXact_Namespace(false);
|
AtEOXact_Namespace(false);
|
||||||
smgrabort();
|
smgrabort();
|
||||||
@ -3956,7 +3952,6 @@ AbortSubTransaction(void)
|
|||||||
|
|
||||||
AtEOXact_GUC(false, s->gucNestLevel);
|
AtEOXact_GUC(false, s->gucNestLevel);
|
||||||
AtEOSubXact_SPI(false, s->subTransactionId);
|
AtEOSubXact_SPI(false, s->subTransactionId);
|
||||||
AtEOXact_xml();
|
|
||||||
AtEOSubXact_on_commit_actions(false, s->subTransactionId,
|
AtEOSubXact_on_commit_actions(false, s->subTransactionId,
|
||||||
s->parent->subTransactionId);
|
s->parent->subTransactionId);
|
||||||
AtEOSubXact_Namespace(false, s->subTransactionId,
|
AtEOSubXact_Namespace(false, s->subTransactionId,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.68.2.11 2009/09/04 10:49:50 heikki Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.68.2.12 2010/03/01 02:21:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -26,29 +26,22 @@
|
|||||||
/*
|
/*
|
||||||
* Notes on memory management:
|
* Notes on memory management:
|
||||||
*
|
*
|
||||||
* Via callbacks, libxml is told to use palloc and friends for memory
|
|
||||||
* management, within a context that we reset at transaction end (and also at
|
|
||||||
* subtransaction abort) to prevent memory leaks. Resetting at transaction or
|
|
||||||
* subtransaction abort is necessary since we might have thrown a longjmp
|
|
||||||
* while some data structures were not linked from anywhere persistent.
|
|
||||||
* Resetting at transaction commit might not be necessary, but seems a good
|
|
||||||
* idea to forestall long-term leaks.
|
|
||||||
*
|
|
||||||
* Sometimes libxml allocates global structures in the hope that it can reuse
|
* Sometimes libxml allocates global structures in the hope that it can reuse
|
||||||
* them later on. Therefore, before resetting LibxmlContext, we must tell
|
* them later on. This makes it impractical to change the xmlMemSetup
|
||||||
* libxml to discard any global data it has. The libxml API documentation is
|
* functions on-the-fly; that is likely to lead to trying to pfree() chunks
|
||||||
* not very good about specifying this, but for now we assume that
|
* allocated with malloc() or vice versa. Since libxml might be used by
|
||||||
* xmlCleanupParser() will get rid of anything we need to worry about.
|
* loadable modules, eg libperl, our only safe choices are to change the
|
||||||
*
|
* functions at postmaster/backend launch or not at all. Since we'd rather
|
||||||
* We use palloc --- which will throw a longjmp on error --- for allocation
|
* not activate libxml in sessions that might never use it, the latter choice
|
||||||
* callbacks that officially should act like malloc, ie, return NULL on
|
* is the preferred one. However, for debugging purposes it can be awfully
|
||||||
* out-of-memory. This is a bit risky since there is a chance of leaving
|
* handy to constrain libxml's allocations to be done in a specific palloc
|
||||||
* persistent libxml data structures in an inconsistent partially-constructed
|
* context, where they're easy to track. Therefore there is code here that
|
||||||
* state, perhaps leading to crash in xmlCleanupParser(). However, as of
|
* can be enabled in debug builds to redirect libxml's allocations into a
|
||||||
* early 2008 it is *known* that libxml can crash on out-of-memory due to
|
* special context LibxmlContext. It's not recommended to turn this on in
|
||||||
* inadequate checks for NULL returns, so this behavior seems the lesser
|
* a production build because of the possibility of bad interactions with
|
||||||
* of two evils.
|
* external modules.
|
||||||
*/
|
*/
|
||||||
|
/* #define USE_LIBXMLCONTEXT */
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
@ -92,19 +85,25 @@ XmlOptionType xmloption;
|
|||||||
#ifdef USE_LIBXML
|
#ifdef USE_LIBXML
|
||||||
|
|
||||||
static StringInfo xml_err_buf = NULL;
|
static StringInfo xml_err_buf = NULL;
|
||||||
static MemoryContext LibxmlContext = NULL;
|
|
||||||
|
|
||||||
static void xml_init(void);
|
|
||||||
static void xml_memory_init(void);
|
|
||||||
static void xml_memory_cleanup(void);
|
|
||||||
static void *xml_palloc(size_t size);
|
|
||||||
static void *xml_repalloc(void *ptr, size_t size);
|
|
||||||
static void xml_pfree(void *ptr);
|
|
||||||
static char *xml_pstrdup(const char *string);
|
|
||||||
static void xml_ereport(int level, int sqlcode, const char *msg);
|
static void xml_ereport(int level, int sqlcode, const char *msg);
|
||||||
static void xml_errorHandler(void *ctxt, const char *msg,...);
|
static void xml_errorHandler(void *ctxt, const char *msg,...);
|
||||||
static void xml_ereport_by_code(int level, int sqlcode,
|
static void xml_ereport_by_code(int level, int sqlcode,
|
||||||
const char *msg, int errcode);
|
const char *msg, int errcode);
|
||||||
|
|
||||||
|
#ifdef USE_LIBXMLCONTEXT
|
||||||
|
|
||||||
|
static MemoryContext LibxmlContext = NULL;
|
||||||
|
|
||||||
|
static void xml_memory_init(void);
|
||||||
|
static void *xml_palloc(size_t size);
|
||||||
|
static void *xml_repalloc(void *ptr, size_t size);
|
||||||
|
static void xml_pfree(void *ptr);
|
||||||
|
static char *xml_pstrdup(const char *string);
|
||||||
|
|
||||||
|
#endif /* USE_LIBXMLCONTEXT */
|
||||||
|
|
||||||
|
static void xml_init(void);
|
||||||
static xmlChar *xml_text2xmlChar(text *in);
|
static xmlChar *xml_text2xmlChar(text *in);
|
||||||
static int parse_xml_decl(const xmlChar * str, size_t *lenp,
|
static int parse_xml_decl(const xmlChar * str, size_t *lenp,
|
||||||
xmlChar ** version, xmlChar ** encoding, int *standalone);
|
xmlChar ** version, xmlChar ** encoding, int *standalone);
|
||||||
@ -154,15 +153,15 @@ static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result,
|
|||||||
#ifdef USE_LIBXML
|
#ifdef USE_LIBXML
|
||||||
|
|
||||||
static int
|
static int
|
||||||
xmlChar_to_encoding(xmlChar * encoding_name)
|
xmlChar_to_encoding(const xmlChar *encoding_name)
|
||||||
{
|
{
|
||||||
int encoding = pg_char_to_encoding((char *) encoding_name);
|
int encoding = pg_char_to_encoding((const char *) encoding_name);
|
||||||
|
|
||||||
if (encoding < 0)
|
if (encoding < 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("invalid encoding name \"%s\"",
|
errmsg("invalid encoding name \"%s\"",
|
||||||
(char *) encoding_name)));
|
(const char *) encoding_name)));
|
||||||
return encoding;
|
return encoding;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -587,8 +586,8 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
|
|||||||
int i;
|
int i;
|
||||||
ListCell *arg;
|
ListCell *arg;
|
||||||
ListCell *narg;
|
ListCell *narg;
|
||||||
xmlBufferPtr buf;
|
xmlBufferPtr buf = NULL;
|
||||||
xmlTextWriterPtr writer;
|
xmlTextWriterPtr writer = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We first evaluate all the arguments, then start up libxml and create
|
* We first evaluate all the arguments, then start up libxml and create
|
||||||
@ -635,8 +634,16 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
|
|||||||
/* now safe to run libxml */
|
/* now safe to run libxml */
|
||||||
xml_init();
|
xml_init();
|
||||||
|
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
buf = xmlBufferCreate();
|
buf = xmlBufferCreate();
|
||||||
|
if (!buf)
|
||||||
|
xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
|
||||||
|
"could not allocate xmlBuffer");
|
||||||
writer = xmlNewTextWriterMemory(buf, 0);
|
writer = xmlNewTextWriterMemory(buf, 0);
|
||||||
|
if (!writer)
|
||||||
|
xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
|
||||||
|
"could not allocate xmlTextWriter");
|
||||||
|
|
||||||
xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
|
xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
|
||||||
|
|
||||||
@ -662,9 +669,23 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
|
|||||||
}
|
}
|
||||||
|
|
||||||
xmlTextWriterEndElement(writer);
|
xmlTextWriterEndElement(writer);
|
||||||
|
|
||||||
|
/* we MUST do this now to flush data out to the buffer ... */
|
||||||
xmlFreeTextWriter(writer);
|
xmlFreeTextWriter(writer);
|
||||||
|
writer = NULL;
|
||||||
|
|
||||||
result = xmlBuffer_to_xmltype(buf);
|
result = xmlBuffer_to_xmltype(buf);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
if (writer)
|
||||||
|
xmlFreeTextWriter(writer);
|
||||||
|
if (buf)
|
||||||
|
xmlBufferFree(buf);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
|
||||||
xmlBufferFree(buf);
|
xmlBufferFree(buf);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -821,6 +842,7 @@ xml_is_document(xmltype *arg)
|
|||||||
xmlDocPtr doc = NULL;
|
xmlDocPtr doc = NULL;
|
||||||
MemoryContext ccxt = CurrentMemoryContext;
|
MemoryContext ccxt = CurrentMemoryContext;
|
||||||
|
|
||||||
|
/* We want to catch ereport(INVALID_XML_DOCUMENT) and return false */
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true,
|
doc = xml_parse((text *) arg, XMLOPTION_DOCUMENT, true,
|
||||||
@ -858,19 +880,6 @@ xml_is_document(xmltype *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* xml cleanup function for transaction end. This is also called on
|
|
||||||
* subtransaction abort; see notes at top of file for rationale.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
AtEOXact_xml(void)
|
|
||||||
{
|
|
||||||
#ifdef USE_LIBXML
|
|
||||||
xml_memory_cleanup();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_LIBXML
|
#ifdef USE_LIBXML
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -908,8 +917,10 @@ xml_init(void)
|
|||||||
/* Now that xml_err_buf exists, safe to call xml_errorHandler */
|
/* Now that xml_err_buf exists, safe to call xml_errorHandler */
|
||||||
xmlSetGenericErrorFunc(NULL, xml_errorHandler);
|
xmlSetGenericErrorFunc(NULL, xml_errorHandler);
|
||||||
|
|
||||||
|
#ifdef USE_LIBXMLCONTEXT
|
||||||
/* Set up memory allocation our way, too */
|
/* Set up memory allocation our way, too */
|
||||||
xml_memory_init();
|
xml_memory_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Check library compatibility */
|
/* Check library compatibility */
|
||||||
LIBXML_TEST_VERSION;
|
LIBXML_TEST_VERSION;
|
||||||
@ -923,14 +934,13 @@ xml_init(void)
|
|||||||
resetStringInfo(xml_err_buf);
|
resetStringInfo(xml_err_buf);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We re-establish the callback functions every time. This makes it
|
* We re-establish the error callback function every time. This makes
|
||||||
* safe for other subsystems (PL/Perl, say) to also use libxml with
|
* it safe for other subsystems (PL/Perl, say) to also use libxml with
|
||||||
* their own callbacks ... so long as they likewise set up the
|
* their own callbacks ... so long as they likewise set up the
|
||||||
* callbacks on every use. It's cheap enough to not be worth worrying
|
* callbacks on every use. It's cheap enough to not be worth worrying
|
||||||
* about, anyway.
|
* about, anyway.
|
||||||
*/
|
*/
|
||||||
xmlSetGenericErrorFunc(NULL, xml_errorHandler);
|
xmlSetGenericErrorFunc(NULL, xml_errorHandler);
|
||||||
xml_memory_init();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1142,7 +1152,7 @@ static bool
|
|||||||
print_xml_decl(StringInfo buf, const xmlChar * version,
|
print_xml_decl(StringInfo buf, const xmlChar * version,
|
||||||
pg_enc encoding, int standalone)
|
pg_enc encoding, int standalone)
|
||||||
{
|
{
|
||||||
xml_init();
|
xml_init(); /* why is this here? */
|
||||||
|
|
||||||
if ((version && strcmp((char *) version, PG_XML_DEFAULT_VERSION) != 0)
|
if ((version && strcmp((char *) version, PG_XML_DEFAULT_VERSION) != 0)
|
||||||
|| (encoding && encoding != PG_UTF8)
|
|| (encoding && encoding != PG_UTF8)
|
||||||
@ -1181,7 +1191,10 @@ print_xml_decl(StringInfo buf, const xmlChar * version,
|
|||||||
/*
|
/*
|
||||||
* Convert a C string to XML internal representation
|
* Convert a C string to XML internal representation
|
||||||
*
|
*
|
||||||
* TODO maybe, libxml2's xmlreader is better? (do not construct DOM,
|
* Note: it is caller's responsibility to xmlFreeDoc() the result,
|
||||||
|
* else a permanent memory leak will ensue!
|
||||||
|
*
|
||||||
|
* TODO maybe libxml2's xmlreader is better? (do not construct DOM,
|
||||||
* yet do not use SAX - see xmlreader.c)
|
* yet do not use SAX - see xmlreader.c)
|
||||||
*/
|
*/
|
||||||
static xmlDocPtr
|
static xmlDocPtr
|
||||||
@ -1202,13 +1215,18 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
|
|||||||
encoding,
|
encoding,
|
||||||
PG_UTF8);
|
PG_UTF8);
|
||||||
|
|
||||||
|
/* Start up libxml and its parser (no-ops if already done) */
|
||||||
xml_init();
|
xml_init();
|
||||||
xmlInitParser();
|
xmlInitParser();
|
||||||
|
|
||||||
ctxt = xmlNewParserCtxt();
|
ctxt = xmlNewParserCtxt();
|
||||||
if (ctxt == NULL)
|
if (ctxt == NULL)
|
||||||
xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
|
xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
|
||||||
"could not allocate parser context");
|
"could not allocate parser context");
|
||||||
|
|
||||||
|
/* Use a TRY block to ensure the ctxt is released */
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
if (xmloption_arg == XMLOPTION_DOCUMENT)
|
if (xmloption_arg == XMLOPTION_DOCUMENT)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -1248,9 +1266,19 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
|
|||||||
res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0,
|
res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0,
|
||||||
utf8string + count, NULL);
|
utf8string + count, NULL);
|
||||||
if (res_code != 0)
|
if (res_code != 0)
|
||||||
|
{
|
||||||
|
xmlFreeDoc(doc);
|
||||||
xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT,
|
xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT,
|
||||||
"invalid XML content");
|
"invalid XML content");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
xmlFreeParserCtxt(ctxt);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
|
||||||
xmlFreeParserCtxt(ctxt);
|
xmlFreeParserCtxt(ctxt);
|
||||||
|
|
||||||
@ -1275,18 +1303,16 @@ xml_text2xmlChar(text *in)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_LIBXMLCONTEXT
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Manage the special context used for all libxml allocations
|
* Manage the special context used for all libxml allocations (but only
|
||||||
|
* in special debug builds; see notes at top of file)
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
xml_memory_init(void)
|
xml_memory_init(void)
|
||||||
{
|
{
|
||||||
/*
|
/* Create memory context if not there already */
|
||||||
* 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)
|
if (LibxmlContext == NULL)
|
||||||
LibxmlContext = AllocSetContextCreate(TopMemoryContext,
|
LibxmlContext = AllocSetContextCreate(TopMemoryContext,
|
||||||
"LibxmlContext",
|
"LibxmlContext",
|
||||||
@ -1298,20 +1324,6 @@ xml_memory_init(void)
|
|||||||
xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
|
xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
xml_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
|
* Wrappers for memory management functions
|
||||||
*/
|
*/
|
||||||
@ -1344,6 +1356,8 @@ xml_pstrdup(const char *string)
|
|||||||
return MemoryContextStrdup(LibxmlContext, string);
|
return MemoryContextStrdup(LibxmlContext, string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* USE_LIBXMLCONTEXT */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wrapper for "ereport" function for XML-related errors. The "msg"
|
* Wrapper for "ereport" function for XML-related errors. The "msg"
|
||||||
@ -1773,25 +1787,48 @@ map_sql_value_to_xml_value(Datum value, Oid type)
|
|||||||
case BYTEAOID:
|
case BYTEAOID:
|
||||||
{
|
{
|
||||||
bytea *bstr = DatumGetByteaPP(value);
|
bytea *bstr = DatumGetByteaPP(value);
|
||||||
xmlBufferPtr buf;
|
xmlBufferPtr buf = NULL;
|
||||||
xmlTextWriterPtr writer;
|
xmlTextWriterPtr writer = NULL;
|
||||||
char *result;
|
char *result;
|
||||||
|
|
||||||
xml_init();
|
xml_init();
|
||||||
|
|
||||||
buf = xmlBufferCreate();
|
PG_TRY();
|
||||||
writer = xmlNewTextWriterMemory(buf, 0);
|
{
|
||||||
|
buf = xmlBufferCreate();
|
||||||
|
if (!buf)
|
||||||
|
xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
|
||||||
|
"could not allocate xmlBuffer");
|
||||||
|
writer = xmlNewTextWriterMemory(buf, 0);
|
||||||
|
if (!writer)
|
||||||
|
xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY,
|
||||||
|
"could not allocate xmlTextWriter");
|
||||||
|
|
||||||
if (xmlbinary == XMLBINARY_BASE64)
|
if (xmlbinary == XMLBINARY_BASE64)
|
||||||
xmlTextWriterWriteBase64(writer, VARDATA_ANY(bstr),
|
xmlTextWriterWriteBase64(writer, VARDATA_ANY(bstr),
|
||||||
0, VARSIZE_ANY_EXHDR(bstr));
|
0, VARSIZE_ANY_EXHDR(bstr));
|
||||||
else
|
else
|
||||||
xmlTextWriterWriteBinHex(writer, VARDATA_ANY(bstr),
|
xmlTextWriterWriteBinHex(writer, VARDATA_ANY(bstr),
|
||||||
0, VARSIZE_ANY_EXHDR(bstr));
|
0, VARSIZE_ANY_EXHDR(bstr));
|
||||||
|
|
||||||
|
/* we MUST do this now to flush data out to the buffer */
|
||||||
|
xmlFreeTextWriter(writer);
|
||||||
|
writer = NULL;
|
||||||
|
|
||||||
|
result = pstrdup((const char *) xmlBufferContent(buf));
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
if (writer)
|
||||||
|
xmlFreeTextWriter(writer);
|
||||||
|
if (buf)
|
||||||
|
xmlBufferFree(buf);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
|
||||||
xmlFreeTextWriter(writer);
|
|
||||||
result = pstrdup((const char *) xmlBufferContent(buf));
|
|
||||||
xmlBufferFree(buf);
|
xmlBufferFree(buf);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif /* USE_LIBXML */
|
#endif /* USE_LIBXML */
|
||||||
@ -3239,25 +3276,46 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
|
|||||||
static text *
|
static text *
|
||||||
xml_xmlnodetoxmltype(xmlNodePtr cur)
|
xml_xmlnodetoxmltype(xmlNodePtr cur)
|
||||||
{
|
{
|
||||||
xmlChar *str;
|
|
||||||
xmltype *result;
|
xmltype *result;
|
||||||
size_t len;
|
|
||||||
xmlBufferPtr buf;
|
|
||||||
|
|
||||||
if (cur->type == XML_ELEMENT_NODE)
|
if (cur->type == XML_ELEMENT_NODE)
|
||||||
{
|
{
|
||||||
|
xmlBufferPtr buf;
|
||||||
|
|
||||||
buf = xmlBufferCreate();
|
buf = xmlBufferCreate();
|
||||||
xmlNodeDump(buf, NULL, cur, 0, 1);
|
PG_TRY();
|
||||||
result = xmlBuffer_to_xmltype(buf);
|
{
|
||||||
|
xmlNodeDump(buf, NULL, cur, 0, 1);
|
||||||
|
result = xmlBuffer_to_xmltype(buf);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
xmlBufferFree(buf);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
xmlBufferFree(buf);
|
xmlBufferFree(buf);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
xmlChar *str;
|
||||||
|
|
||||||
str = xmlXPathCastNodeToString(cur);
|
str = xmlXPathCastNodeToString(cur);
|
||||||
len = strlen((char *) str);
|
PG_TRY();
|
||||||
result = (text *) palloc(len + VARHDRSZ);
|
{
|
||||||
SET_VARSIZE(result, len + VARHDRSZ);
|
size_t len;
|
||||||
memcpy(VARDATA(result), str, len);
|
|
||||||
|
len = strlen((char *) str);
|
||||||
|
result = (text *) palloc(len + VARHDRSZ);
|
||||||
|
SET_VARSIZE(result, len + VARHDRSZ);
|
||||||
|
memcpy(VARDATA(result), str, len);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
xmlFree(str);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
xmlFree(str);
|
xmlFree(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3284,11 +3342,11 @@ xpath(PG_FUNCTION_ARGS)
|
|||||||
xmltype *data = PG_GETARG_XML_P(1);
|
xmltype *data = PG_GETARG_XML_P(1);
|
||||||
ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
|
ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
|
||||||
ArrayBuildState *astate = NULL;
|
ArrayBuildState *astate = NULL;
|
||||||
xmlParserCtxtPtr ctxt;
|
xmlParserCtxtPtr ctxt = NULL;
|
||||||
xmlDocPtr doc;
|
xmlDocPtr doc = NULL;
|
||||||
xmlXPathContextPtr xpathctx;
|
xmlXPathContextPtr xpathctx = NULL;
|
||||||
xmlXPathCompExprPtr xpathcomp;
|
xmlXPathCompExprPtr xpathcomp = NULL;
|
||||||
xmlXPathObjectPtr xpathobj;
|
xmlXPathObjectPtr xpathobj = NULL;
|
||||||
char *datastr;
|
char *datastr;
|
||||||
int32 len;
|
int32 len;
|
||||||
int32 xpath_len;
|
int32 xpath_len;
|
||||||
@ -3347,8 +3405,6 @@ xpath(PG_FUNCTION_ARGS)
|
|||||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
errmsg("empty XPath expression")));
|
errmsg("empty XPath expression")));
|
||||||
|
|
||||||
xml_init();
|
|
||||||
|
|
||||||
/* These extra chars for string and xpath_expr allow for hacks below */
|
/* These extra chars for string and xpath_expr allow for hacks below */
|
||||||
|
|
||||||
string = (xmlChar *) palloc((len + 8) * sizeof(xmlChar));
|
string = (xmlChar *) palloc((len + 8) * sizeof(xmlChar));
|
||||||
@ -3357,10 +3413,12 @@ xpath(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
memcpy (string, datastr, len);
|
memcpy (string, datastr, len);
|
||||||
string[len] = '\0';
|
string[len] = '\0';
|
||||||
|
|
||||||
|
|
||||||
|
xml_init();
|
||||||
xmlInitParser();
|
xmlInitParser();
|
||||||
|
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* redundant XML parsing (two parsings for the same value during one
|
* redundant XML parsing (two parsings for the same value during one
|
||||||
* command execution are possible)
|
* command execution are possible)
|
||||||
@ -3478,10 +3536,8 @@ xpath(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
|
xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
|
||||||
if (xpathobj == NULL) /* TODO: reason? */
|
if (xpathobj == NULL) /* TODO: reason? */
|
||||||
ereport(ERROR,
|
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
|
||||||
(errmsg("could not create XPath object")));
|
"could not create XPath object");
|
||||||
|
|
||||||
xmlXPathFreeCompExpr(xpathcomp);
|
|
||||||
|
|
||||||
/* return empty array in cases when nothing is found */
|
/* return empty array in cases when nothing is found */
|
||||||
if (xpathobj->nodesetval == NULL)
|
if (xpathobj->nodesetval == NULL)
|
||||||
@ -3502,8 +3558,25 @@ xpath(PG_FUNCTION_ARGS)
|
|||||||
CurrentMemoryContext);
|
CurrentMemoryContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
if (xpathobj)
|
||||||
|
xmlXPathFreeObject(xpathobj);
|
||||||
|
if (xpathcomp)
|
||||||
|
xmlXPathFreeCompExpr(xpathcomp);
|
||||||
|
if (xpathctx)
|
||||||
|
xmlXPathFreeContext(xpathctx);
|
||||||
|
if (doc)
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
if (ctxt)
|
||||||
|
xmlFreeParserCtxt(ctxt);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
|
||||||
xmlXPathFreeObject(xpathobj);
|
xmlXPathFreeObject(xpathobj);
|
||||||
|
xmlXPathFreeCompExpr(xpathcomp);
|
||||||
xmlXPathFreeContext(xpathctx);
|
xmlXPathFreeContext(xpathctx);
|
||||||
xmlFreeDoc(doc);
|
xmlFreeDoc(doc);
|
||||||
xmlFreeParserCtxt(ctxt);
|
xmlFreeParserCtxt(ctxt);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.23 2008/01/15 18:57:00 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.23.2.1 2010/03/01 02:21:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -75,8 +75,6 @@ extern char *map_sql_identifier_to_xml_name(char *ident, bool fully_escaped, boo
|
|||||||
extern char *map_xml_name_to_sql_identifier(char *name);
|
extern char *map_xml_name_to_sql_identifier(char *name);
|
||||||
extern char *map_sql_value_to_xml_value(Datum value, Oid type);
|
extern char *map_sql_value_to_xml_value(Datum value, Oid type);
|
||||||
|
|
||||||
extern void AtEOXact_xml(void);
|
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
XMLBINARY_BASE64,
|
XMLBINARY_BASE64,
|
||||||
|
Reference in New Issue
Block a user