Skip to content

Commit f702de7

Browse files
committed
[bugfix] The XQueryContext used by the XPath function util:eval must be correctly reset to prevent a leak of Update Listeners which can hold large context sequences from Binding Expressions
1 parent 11eefe8 commit f702de7

3 files changed

Lines changed: 29 additions & 13 deletions

File tree

exist-core/src/main/java/org/exist/xquery/Context.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ public interface Context {
8383

8484
XQueryContext getRootContext();
8585

86+
/**
87+
* Create a new context ("Inner Context") that will share some state with this context.
88+
* After calling this {@link #setShared(boolean)} should be set to true.
89+
*
90+
* @return an inner context.
91+
*/
8692
XQueryContext copyContext();
8793

8894
/**
@@ -338,6 +344,13 @@ public interface Context {
338344
*/
339345
boolean lockDocumentsOnLoad();
340346

347+
/**
348+
* When set as true, it indicates that this Context (often known as the Inner Context)
349+
* shares some state with another Context (often known as the Outer Context).
350+
* This should be set to true after any call to {@link #copyContext()}.
351+
*
352+
* @param shared set to true if this context shares state with another context, false otherwise.
353+
*/
341354
void setShared(boolean shared);
342355

343356
boolean isShared();

exist-core/src/main/java/org/exist/xquery/XQueryContext.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,15 +1483,17 @@ public void reset(final boolean keepGlobals) {
14831483
watchdog.reset();
14841484
}
14851485

1486-
/*
1487-
NOTE: we use `modules` (and not `allModules`) here so as to only reset
1488-
the modules of this module.
1489-
The inner call to `module.reset` will be called on sub-modules
1490-
which in-turn will reset their modules, and so on.
1491-
*/
1492-
for (final Module[] modules : modules.values()) {
1493-
for (final Module module : modules) {
1494-
module.reset(this, keepGlobals);
1486+
if (!isShared) {
1487+
/*
1488+
NOTE: we use `modules` (and not `allModules`) here so as to only reset
1489+
the modules of this module.
1490+
The inner call to `module.reset` will be called on sub-modules
1491+
which in-turn will reset their modules, and so on.
1492+
*/
1493+
for (final Module[] modules : modules.values()) {
1494+
for (final Module module : modules) {
1495+
module.reset(this, keepGlobals);
1496+
}
14951497
}
14961498
}
14971499

@@ -1501,7 +1503,9 @@ public void reset(final boolean keepGlobals) {
15011503

15021504
clearUpdateListeners();
15031505

1504-
profiler.reset();
1506+
if (!isShared) {
1507+
profiler.reset();
1508+
}
15051509

15061510
if (!keepGlobals) {
15071511
httpContext = null;

exist-core/src/main/java/org/exist/xquery/functions/util/Eval.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ private Sequence execute(final DBBroker broker, final XQuery xqueryService, fina
469469
compiled.getContext().prepareForReuse();
470470
}
471471

472-
Sequence sequence = xqueryService.execute(broker, compiled, exprContext, outputProperties, false);
472+
Sequence sequence = xqueryService.execute(broker, compiled, exprContext, outputProperties, true);
473473
ValueSequence newSeq = new ValueSequence();
474474
newSeq.keepUnOrdered(unordered);
475475
boolean hasSupplements = false;
@@ -496,10 +496,9 @@ private Sequence execute(final DBBroker broker, final XQuery xqueryService, fina
496496
if (compiled.getContext() != null) {
497497
compiled.getContext().runCleanupTasks();
498498
}
499+
499500
if (cache) {
500501
pool.returnCompiledXQuery(querySource, compiled);
501-
} else {
502-
compiled.reset();
503502
}
504503
}
505504
}

0 commit comments

Comments
 (0)