aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt')
-rw-r--r--src/backend/utils/adt/datetime.c44
-rw-r--r--src/backend/utils/adt/mcxtfuncs.c426
-rw-r--r--src/backend/utils/adt/pg_locale.c1
-rw-r--r--src/backend/utils/adt/pgstatfuncs.c4
-rw-r--r--src/backend/utils/adt/regexp.c38
-rw-r--r--src/backend/utils/adt/xml.c21
6 files changed, 111 insertions, 423 deletions
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 793d8a9adcc..680fee2a844 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -702,9 +702,18 @@ ParseFraction(char *cp, double *frac)
}
else
{
+ /*
+ * On the other hand, let's reject anything that's not digits after
+ * the ".". strtod is happy with input like ".123e9", but that'd
+ * break callers' expectation that the result is in 0..1. (It's quite
+ * difficult to get here with such input, but not impossible.)
+ */
+ if (strspn(cp + 1, "0123456789") != strlen(cp + 1))
+ return DTERR_BAD_FORMAT;
+
errno = 0;
*frac = strtod(cp, &cp);
- /* check for parse failure */
+ /* check for parse failure (probably redundant given prior check) */
if (*cp != '\0' || errno != 0)
return DTERR_BAD_FORMAT;
}
@@ -2959,30 +2968,27 @@ DecodeNumberField(int len, char *str, int fmask,
char *cp;
/*
+ * This function was originally meant to cope only with DTK_NUMBER fields,
+ * but we now sometimes abuse it to parse (parts of) DTK_DATE fields,
+ * which can contain letters and other punctuation. Reject if it's not a
+ * valid DTK_NUMBER, that is digits and decimal point(s). (ParseFraction
+ * will reject if there's more than one decimal point.)
+ */
+ if (strspn(str, "0123456789.") != len)
+ return DTERR_BAD_FORMAT;
+
+ /*
* Have a decimal point? Then this is a date or something with a seconds
* field...
*/
if ((cp = strchr(str, '.')) != NULL)
{
- /*
- * Can we use ParseFractionalSecond here? Not clear whether trailing
- * junk should be rejected ...
- */
- if (cp[1] == '\0')
- {
- /* avoid assuming that strtod will accept "." */
- *fsec = 0;
- }
- else
- {
- double frac;
+ int dterr;
- errno = 0;
- frac = strtod(cp, NULL);
- if (errno != 0)
- return DTERR_BAD_FORMAT;
- *fsec = rint(frac * 1000000);
- }
+ /* Convert the fraction and store at *fsec */
+ dterr = ParseFractionalSecond(cp, fsec);
+ if (dterr)
+ return dterr;
/* Now truncate off the fraction for further processing */
*cp = '\0';
len = strlen(str);
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 7ec2c225016..396c2f223b4 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -15,27 +15,30 @@
#include "postgres.h"
-#include "access/twophase.h"
-#include "catalog/pg_authid_d.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
-#include "miscadmin.h"
#include "storage/proc.h"
#include "storage/procarray.h"
-#include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
-#include "utils/memutils.h"
-#include "utils/wait_event_types.h"
/* ----------
* The max bytes for showing identifiers of MemoryContext.
* ----------
*/
#define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE 1024
-struct MemoryStatsBackendState *memCxtState = NULL;
-struct MemoryStatsCtl *memCxtArea = NULL;
+
+/*
+ * MemoryContextId
+ * Used for storage of transient identifiers for
+ * pg_get_backend_memory_contexts.
+ */
+typedef struct MemoryContextId
+{
+ MemoryContext context;
+ int context_id;
+} MemoryContextId;
/*
* int_list_to_array
@@ -86,7 +89,7 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
*/
for (MemoryContext cur = context; cur != NULL; cur = cur->parent)
{
- MemoryStatsContextId *entry;
+ MemoryContextId *entry;
bool found;
entry = hash_search(context_id_lookup, &cur, HASH_FIND, &found);
@@ -140,51 +143,36 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
else
nulls[1] = true;
- type = ContextTypeToString(context->type);
-
- values[2] = CStringGetTextDatum(type);
- values[3] = Int32GetDatum(list_length(path)); /* level */
- values[4] = int_list_to_array(path);
- values[5] = Int64GetDatum(stat.totalspace);
- values[6] = Int64GetDatum(stat.nblocks);
- values[7] = Int64GetDatum(stat.freespace);
- values[8] = Int64GetDatum(stat.freechunks);
- values[9] = Int64GetDatum(stat.totalspace - stat.freespace);
-
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
- list_free(path);
-}
-
-/*
- * ContextTypeToString
- * Returns a textual representation of a context type
- *
- * This should cover the same types as MemoryContextIsValid.
- */
-const char *
-ContextTypeToString(NodeTag type)
-{
- const char *context_type;
-
- switch (type)
+ switch (context->type)
{
case T_AllocSetContext:
- context_type = "AllocSet";
+ type = "AllocSet";
break;
case T_GenerationContext:
- context_type = "Generation";
+ type = "Generation";
break;
case T_SlabContext:
- context_type = "Slab";
+ type = "Slab";
break;
case T_BumpContext:
- context_type = "Bump";
+ type = "Bump";
break;
default:
- context_type = "???";
+ type = "???";
break;
}
- return context_type;
+
+ values[2] = CStringGetTextDatum(type);
+ values[3] = Int32GetDatum(list_length(path)); /* level */
+ values[4] = int_list_to_array(path);
+ values[5] = Int64GetDatum(stat.totalspace);
+ values[6] = Int64GetDatum(stat.nblocks);
+ values[7] = Int64GetDatum(stat.freespace);
+ values[8] = Int64GetDatum(stat.freechunks);
+ values[9] = Int64GetDatum(stat.totalspace - stat.freespace);
+
+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ list_free(path);
}
/*
@@ -201,7 +189,7 @@ pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
HTAB *context_id_lookup;
ctl.keysize = sizeof(MemoryContext);
- ctl.entrysize = sizeof(MemoryStatsContextId);
+ ctl.entrysize = sizeof(MemoryContextId);
ctl.hcxt = CurrentMemoryContext;
context_id_lookup = hash_create("pg_get_backend_memory_contexts",
@@ -228,7 +216,7 @@ pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
foreach_ptr(MemoryContextData, cur, contexts)
{
- MemoryStatsContextId *entry;
+ MemoryContextId *entry;
bool found;
/*
@@ -236,8 +224,8 @@ pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
* PutMemoryContextsStatsTupleStore needs this to populate the "path"
* column with the parent context_ids.
*/
- entry = (MemoryStatsContextId *) hash_search(context_id_lookup, &cur,
- HASH_ENTER, &found);
+ entry = (MemoryContextId *) hash_search(context_id_lookup, &cur,
+ HASH_ENTER, &found);
entry->context_id = context_id++;
Assert(!found);
@@ -317,349 +305,3 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(true);
}
-
-/*
- * pg_get_process_memory_contexts
- * Signal a backend or an auxiliary process to send its memory contexts,
- * wait for the results and display them.
- *
- * By default, only superusers or users with ROLE_PG_READ_ALL_STATS are allowed
- * to signal a process to return the memory contexts. This is because allowing
- * any users to issue this request at an unbounded rate would cause lots of
- * requests to be sent, which can lead to denial of service. Additional roles
- * can be permitted with GRANT.
- *
- * On receipt of this signal, a backend or an auxiliary process sets the flag
- * in the signal handler, which causes the next CHECK_FOR_INTERRUPTS()
- * or process-specific interrupt handler to copy the memory context details
- * to a dynamic shared memory space.
- *
- * We have defined a limit on DSA memory that could be allocated per process -
- * if the process has more memory contexts than what can fit in the allocated
- * size, the excess contexts are summarized and represented as cumulative total
- * at the end of the buffer.
- *
- * After sending the signal, wait on a condition variable. The publishing
- * backend, after copying the data to shared memory, sends signal on that
- * condition variable. There is one condition variable per publishing backend.
- * Once the condition variable is signalled, check if the latest memory context
- * information is available and display.
- *
- * If the publishing backend does not respond before the condition variable
- * times out, which is set to MEMSTATS_WAIT_TIMEOUT, retry given that there is
- * time left within the timeout specified by the user, before giving up and
- * returning previously published statistics, if any. If no previous statistics
- * exist, return NULL.
- */
-#define MEMSTATS_WAIT_TIMEOUT 100
-Datum
-pg_get_process_memory_contexts(PG_FUNCTION_ARGS)
-{
- int pid = PG_GETARG_INT32(0);
- bool summary = PG_GETARG_BOOL(1);
- double timeout = PG_GETARG_FLOAT8(2);
- PGPROC *proc;
- ProcNumber procNumber = INVALID_PROC_NUMBER;
- bool proc_is_aux = false;
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- MemoryStatsEntry *memcxt_info;
- TimestampTz start_timestamp;
-
- /*
- * See if the process with given pid is a backend or an auxiliary process
- * and remember the type for when we requery the process later.
- */
- proc = BackendPidGetProc(pid);
- if (proc == NULL)
- {
- proc = AuxiliaryPidGetProc(pid);
- proc_is_aux = true;
- }
-
- /*
- * BackendPidGetProc() and AuxiliaryPidGetProc() return NULL if the pid
- * isn't valid; this is however not a problem and leave with a WARNING.
- * See comment in pg_log_backend_memory_contexts for a discussion on this.
- */
- if (proc == NULL)
- {
- /*
- * This is just a warning so a loop-through-resultset will not abort
- * if one backend terminated on its own during the run.
- */
- ereport(WARNING,
- errmsg("PID %d is not a PostgreSQL server process", pid));
- PG_RETURN_NULL();
- }
-
- InitMaterializedSRF(fcinfo, 0);
-
- procNumber = GetNumberFromPGProc(proc);
-
- LWLockAcquire(&memCxtState[procNumber].lw_lock, LW_EXCLUSIVE);
- memCxtState[procNumber].summary = summary;
- LWLockRelease(&memCxtState[procNumber].lw_lock);
-
- start_timestamp = GetCurrentTimestamp();
-
- /*
- * Send a signal to a PostgreSQL process, informing it we want it to
- * produce information about its memory contexts.
- */
- if (SendProcSignal(pid, PROCSIG_GET_MEMORY_CONTEXT, procNumber) < 0)
- {
- ereport(WARNING,
- errmsg("could not send signal to process %d: %m", pid));
- PG_RETURN_NULL();
- }
-
- /*
- * Even if the proc has published statistics, the may not be due to the
- * current request, but previously published stats. Check if the stats
- * are updated by comparing the timestamp, if the stats are newer than our
- * previously recorded timestamp from before sending the procsignal, they
- * must by definition be updated. Wait for the timeout specified by the
- * user, following which display old statistics if available or return
- * NULL.
- */
- while (1)
- {
- long msecs;
-
- /*
- * We expect to come out of sleep when the requested process has
- * finished publishing the statistics, verified using the valid DSA
- * pointer.
- *
- * Make sure that the information belongs to pid we requested
- * information for, Otherwise loop back and wait for the server
- * process to finish publishing statistics.
- */
- LWLockAcquire(&memCxtState[procNumber].lw_lock, LW_EXCLUSIVE);
-
- /*
- * Note in procnumber.h file says that a procNumber can be re-used for
- * a different backend immediately after a backend exits. In case an
- * old process' data was there and not updated by the current process
- * in the slot identified by the procNumber, the pid of the requested
- * process and the proc_id might not match.
- */
- if (memCxtState[procNumber].proc_id == pid)
- {
- /*
- * Break if the latest stats have been read, indicated by
- * statistics timestamp being newer than the current request
- * timestamp.
- */
- msecs = TimestampDifferenceMilliseconds(start_timestamp,
- memCxtState[procNumber].stats_timestamp);
-
- if (DsaPointerIsValid(memCxtState[procNumber].memstats_dsa_pointer)
- && msecs > 0)
- break;
- }
- LWLockRelease(&memCxtState[procNumber].lw_lock);
-
- /*
- * Recheck the state of the backend before sleeping on the condition
- * variable to ensure the process is still alive. Only check the
- * relevant process type based on the earlier PID check.
- */
- if (proc_is_aux)
- proc = AuxiliaryPidGetProc(pid);
- else
- proc = BackendPidGetProc(pid);
-
- /*
- * The process ending during memory context processing is not an
- * error.
- */
- if (proc == NULL)
- {
- ereport(WARNING,
- errmsg("PID %d is no longer a PostgreSQL server process",
- pid));
- PG_RETURN_NULL();
- }
-
- msecs = TimestampDifferenceMilliseconds(start_timestamp, GetCurrentTimestamp());
-
- /*
- * If we haven't already exceeded the timeout value, sleep for the
- * remainder of the timeout on the condition variable.
- */
- if (msecs > 0 && msecs < (timeout * 1000))
- {
- /*
- * Wait for the timeout as defined by the user. If no updated
- * statistics are available within the allowed time then display
- * previously published statistics if there are any. If no
- * previous statistics are available then return NULL. The timer
- * is defined in milliseconds since that's what the condition
- * variable sleep uses.
- */
- if (ConditionVariableTimedSleep(&memCxtState[procNumber].memcxt_cv,
- ((timeout * 1000) - msecs), WAIT_EVENT_MEM_CXT_PUBLISH))
- {
- LWLockAcquire(&memCxtState[procNumber].lw_lock, LW_EXCLUSIVE);
- /* Displaying previously published statistics if available */
- if (DsaPointerIsValid(memCxtState[procNumber].memstats_dsa_pointer))
- break;
- else
- {
- LWLockRelease(&memCxtState[procNumber].lw_lock);
- PG_RETURN_NULL();
- }
- }
- }
- else
- {
- LWLockAcquire(&memCxtState[procNumber].lw_lock, LW_EXCLUSIVE);
- /* Displaying previously published statistics if available */
- if (DsaPointerIsValid(memCxtState[procNumber].memstats_dsa_pointer))
- break;
- else
- {
- LWLockRelease(&memCxtState[procNumber].lw_lock);
- PG_RETURN_NULL();
- }
- }
- }
-
- /*
- * We should only reach here with a valid DSA handle, either containing
- * updated statistics or previously published statistics (identified by
- * the timestamp.
- */
- Assert(memCxtArea->memstats_dsa_handle != DSA_HANDLE_INVALID);
- /* Attach to the dsa area if we have not already done so */
- if (MemoryStatsDsaArea == NULL)
- {
- MemoryContext oldcontext = CurrentMemoryContext;
-
- MemoryContextSwitchTo(TopMemoryContext);
- MemoryStatsDsaArea = dsa_attach(memCxtArea->memstats_dsa_handle);
- MemoryContextSwitchTo(oldcontext);
- dsa_pin_mapping(MemoryStatsDsaArea);
- }
-
- /*
- * Backend has finished publishing the stats, project them.
- */
- memcxt_info = (MemoryStatsEntry *)
- dsa_get_address(MemoryStatsDsaArea, memCxtState[procNumber].memstats_dsa_pointer);
-
-#define PG_GET_PROCESS_MEMORY_CONTEXTS_COLS 12
- for (int i = 0; i < memCxtState[procNumber].total_stats; i++)
- {
- ArrayType *path_array;
- int path_length;
- Datum values[PG_GET_PROCESS_MEMORY_CONTEXTS_COLS];
- bool nulls[PG_GET_PROCESS_MEMORY_CONTEXTS_COLS];
- char *name;
- char *ident;
- Datum *path_datum = NULL;
- int *path_int = NULL;
-
- memset(values, 0, sizeof(values));
- memset(nulls, 0, sizeof(nulls));
-
- if (DsaPointerIsValid(memcxt_info[i].name))
- {
- name = (char *) dsa_get_address(MemoryStatsDsaArea, memcxt_info[i].name);
- values[0] = CStringGetTextDatum(name);
- }
- else
- nulls[0] = true;
-
- if (DsaPointerIsValid(memcxt_info[i].ident))
- {
- ident = (char *) dsa_get_address(MemoryStatsDsaArea, memcxt_info[i].ident);
- values[1] = CStringGetTextDatum(ident);
- }
- else
- nulls[1] = true;
-
- values[2] = CStringGetTextDatum(ContextTypeToString(memcxt_info[i].type));
-
- path_length = memcxt_info[i].path_length;
- path_datum = (Datum *) palloc(path_length * sizeof(Datum));
- if (DsaPointerIsValid(memcxt_info[i].path))
- {
- path_int = (int *) dsa_get_address(MemoryStatsDsaArea, memcxt_info[i].path);
- for (int j = 0; j < path_length; j++)
- path_datum[j] = Int32GetDatum(path_int[j]);
- path_array = construct_array_builtin(path_datum, path_length, INT4OID);
- values[3] = PointerGetDatum(path_array);
- }
- else
- nulls[3] = true;
-
- values[4] = Int32GetDatum(memcxt_info[i].levels);
- values[5] = Int64GetDatum(memcxt_info[i].totalspace);
- values[6] = Int64GetDatum(memcxt_info[i].nblocks);
- values[7] = Int64GetDatum(memcxt_info[i].freespace);
- values[8] = Int64GetDatum(memcxt_info[i].freechunks);
- values[9] = Int64GetDatum(memcxt_info[i].totalspace -
- memcxt_info[i].freespace);
- values[10] = Int32GetDatum(memcxt_info[i].num_agg_stats);
- values[11] = TimestampTzGetDatum(memCxtState[procNumber].stats_timestamp);
-
- tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
- values, nulls);
- }
- LWLockRelease(&memCxtState[procNumber].lw_lock);
-
- ConditionVariableCancelSleep();
-
- PG_RETURN_NULL();
-}
-
-Size
-MemoryContextReportingShmemSize(void)
-{
- Size sz = 0;
- Size TotalProcs = 0;
-
- TotalProcs = add_size(TotalProcs, NUM_AUXILIARY_PROCS);
- TotalProcs = add_size(TotalProcs, MaxBackends);
- sz = add_size(sz, mul_size(TotalProcs, sizeof(MemoryStatsBackendState)));
-
- sz = add_size(sz, sizeof(MemoryStatsCtl));
-
- return sz;
-}
-
-/*
- * Initialize shared memory for displaying memory context statistics
- */
-void
-MemoryContextReportingShmemInit(void)
-{
- bool found;
-
- memCxtArea = (MemoryStatsCtl *)
- ShmemInitStruct("MemoryStatsCtl",
- sizeof(MemoryStatsCtl), &found);
-
- if (!found)
- {
- LWLockInitialize(&memCxtArea->lw_lock, LWTRANCHE_MEMORY_CONTEXT_REPORTING_STATE);
- memCxtArea->memstats_dsa_handle = DSA_HANDLE_INVALID;
- }
-
- memCxtState = (MemoryStatsBackendState *)
- ShmemInitStruct("MemoryStatsBackendState",
- ((MaxBackends + NUM_AUXILIARY_PROCS) * sizeof(MemoryStatsBackendState)),
- &found);
-
- if (found)
- return;
-
- for (int i = 0; i < (MaxBackends + NUM_AUXILIARY_PROCS); i++)
- {
- ConditionVariableInit(&memCxtState[i].memcxt_cv);
- LWLockInitialize(&memCxtState[i].lw_lock, LWTRANCHE_MEMORY_CONTEXT_REPORTING_PROC);
- memCxtState[i].memstats_dsa_pointer = InvalidDsaPointer;
- }
-}
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index a858f27cadc..f5e31c433a0 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -46,6 +46,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
+#include "utils/relcache.h"
#include "utils/syscache.h"
#ifdef WIN32
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 97af7c6554f..e980109f245 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -640,10 +640,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
values[28] = BoolGetDatum(false); /* GSS credentials not
* delegated */
}
- if (beentry->st_query_id == 0)
+ if (beentry->st_query_id == INT64CONST(0))
nulls[30] = true;
else
- values[30] = UInt64GetDatum(beentry->st_query_id);
+ values[30] = Int64GetDatum(beentry->st_query_id);
}
else
{
diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
index edee1f7880b..6e2864cbbda 100644
--- a/src/backend/utils/adt/regexp.c
+++ b/src/backend/utils/adt/regexp.c
@@ -773,8 +773,11 @@ similar_escape_internal(text *pat_text, text *esc_text)
int plen,
elen;
bool afterescape = false;
- bool incharclass = false;
int nquotes = 0;
+ int charclass_depth = 0; /* Nesting level of character classes,
+ * encompassed by square brackets */
+ int charclass_start = 0; /* State of the character class start,
+ * for carets */
p = VARDATA_ANY(pat_text);
plen = VARSIZE_ANY_EXHDR(pat_text);
@@ -904,7 +907,7 @@ similar_escape_internal(text *pat_text, text *esc_text)
/* fast path */
if (afterescape)
{
- if (pchar == '"' && !incharclass) /* escape-double-quote? */
+ if (pchar == '"' && charclass_depth < 1) /* escape-double-quote? */
{
/* emit appropriate part separator, per notes above */
if (nquotes == 0)
@@ -953,18 +956,41 @@ similar_escape_internal(text *pat_text, text *esc_text)
/* SQL escape character; do not send to output */
afterescape = true;
}
- else if (incharclass)
+ else if (charclass_depth > 0)
{
if (pchar == '\\')
*r++ = '\\';
*r++ = pchar;
- if (pchar == ']')
- incharclass = false;
+
+ /*
+ * Ignore a closing bracket at the start of a character class.
+ * Such a bracket is taken literally rather than closing the
+ * class. "charclass_start" is 1 right at the beginning of a
+ * class and 2 after an initial caret.
+ */
+ if (pchar == ']' && charclass_start > 2)
+ charclass_depth--;
+ else if (pchar == '[')
+ charclass_depth++;
+
+ /*
+ * If there is a caret right after the opening bracket, it negates
+ * the character class, but a following closing bracket should
+ * still be treated as a normal character. That holds only for
+ * the first caret, so only the values 1 and 2 mean that closing
+ * brackets should be taken literally.
+ */
+ if (pchar == '^')
+ charclass_start++;
+ else
+ charclass_start = 3; /* definitely past the start */
}
else if (pchar == '[')
{
+ /* start of a character class */
*r++ = pchar;
- incharclass = true;
+ charclass_depth++;
+ charclass_start = 1;
}
else if (pchar == '%')
{
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index db8d0d6a7e8..a4150bff2ea 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -754,6 +754,7 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
* content nodes, and then iterate over the nodes.
*/
xmlNodePtr root;
+ xmlNodePtr oldroot;
xmlNodePtr newline;
root = xmlNewNode(NULL, (const xmlChar *) "content-root");
@@ -761,8 +762,14 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
"could not allocate xml node");
- /* This attaches root to doc, so we need not free it separately. */
- xmlDocSetRootElement(doc, root);
+ /*
+ * This attaches root to doc, so we need not free it separately...
+ * but instead, we have to free the old root if there was one.
+ */
+ oldroot = xmlDocSetRootElement(doc, root);
+ if (oldroot != NULL)
+ xmlFreeNode(oldroot);
+
xmlAddChildList(root, content_nodes);
/*
@@ -1850,6 +1857,7 @@ xml_parse(text *data, XmlOptionType xmloption_arg,
else
{
xmlNodePtr root;
+ xmlNodePtr oldroot PG_USED_FOR_ASSERTS_ONLY;
/* set up document with empty root node to be the context node */
doc = xmlNewDoc(version);
@@ -1868,8 +1876,13 @@ xml_parse(text *data, XmlOptionType xmloption_arg,
if (root == NULL || xmlerrcxt->err_occurred)
xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
"could not allocate xml node");
- /* This attaches root to doc, so we need not free it separately. */
- xmlDocSetRootElement(doc, root);
+
+ /*
+ * This attaches root to doc, so we need not free it separately;
+ * and there can't yet be any old root to free.
+ */
+ oldroot = xmlDocSetRootElement(doc, root);
+ Assert(oldroot == NULL);
/* allow empty content */
if (*(utf8string + count))