Skip to content

Commit 916c196

Browse files
authored
Merge pull request #108 from evolvedbinary/7.x.x/hotfix/ndw-rest-server-issues
[7.x.x] Improve setting XQuery external variables and serialization of typed XDM items
2 parents 781456c + b555e1c commit 916c196

File tree

179 files changed

+14858
-3176
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

179 files changed

+14858
-3176
lines changed

exist-core/pom.xml

Lines changed: 174 additions & 17 deletions
Large diffs are not rendered by default.

exist-core/src/main/antlr/org/exist/xquery/parser/XQueryTree.g

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -532,9 +532,8 @@ throws PermissionDeniedException, EXistException, XPathException
532532
}
533533
declaredGlobalVars.add(qn);
534534
}
535-
{ List annots = new ArrayList(); }
536-
(annotations [annots]
537-
)?
535+
{ List annots = new ArrayList(); }
536+
(annotations [annots])?
538537
(
539538
#(
540539
"as"
@@ -564,22 +563,11 @@ throws PermissionDeniedException, EXistException, XPathException
564563
step=ext:expr [defaultValue]
565564
)?
566565
{
567-
// variable may be declared in static context: retrieve and set its sequence type
568-
Variable external = null;
569-
try {
570-
external = context.resolveVariable(qname.getText());
571-
if (external != null) {
572-
external.setSequenceType(type);
573-
}
574-
} catch (XPathException ignoredException) {
575-
}
576-
577566
final VariableDeclaration decl = new VariableDeclaration(context, qn, defaultValue);
578567
decl.setSequenceType(type);
568+
decl.setExternal(true);
579569
decl.setASTNode(ext);
580-
if (external == null) {
581-
path.add(decl);
582-
}
570+
path.add(decl);
583571
if(myModule != null) {
584572
myModule.declareVariable(qn, decl);
585573
}

exist-core/src/main/java/org/exist/collections/triggers/XQueryTrigger.java

Lines changed: 156 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; version 2.1.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this library; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
*
21+
* NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+
* The original license header is included below.
23+
*
24+
* =====================================================================
25+
*
226
* eXist-db Open Source Native XML Database
327
* Copyright (C) 2001 The eXist-db Authors
428
*
@@ -49,6 +73,8 @@
4973
import org.exist.xquery.value.Sequence;
5074
import org.exist.xquery.value.StringValue;
5175

76+
import javax.annotation.Nullable;
77+
5278
import static com.evolvedbinary.j8fu.tuple.Tuple.Tuple;
5379

5480
/**
@@ -232,33 +258,47 @@ private void prepare(final TriggerEvent event, final DBBroker broker, final Txn
232258
LOG.warn(e.getMessage());
233259
return;
234260
}
235-
236-
final XQueryContext context = new XQueryContext(broker.getBrokerPool());
237-
CompiledXQuery compiledQuery = null;
238-
try {
239-
//compile the XQuery
240-
compiledQuery = service.compile(context, query);
241-
declareExternalVariables(context, TriggerPhase.BEFORE, event, src, dst, isCollection);
242-
243-
} catch (final XPathException | IOException | PermissionDeniedException e) {
244-
TriggerStatePerThread.clear();
245-
throw new TriggerException(PREPARE_EXCEPTION_MESSAGE, e);
246-
}
247261

248-
//execute the XQuery
262+
@Nullable CompiledXQuery compiled = null;
263+
@Nullable XQueryContext context = null;
249264
try {
250-
//TODO : should we provide another contextSet ?
251-
final NodeSet contextSet = NodeSet.EMPTY_SET;
252-
service.execute(broker, compiledQuery, contextSet);
253-
//TODO : should we have a special processing ?
254-
if (LOG.isDebugEnabled()) {
255-
LOG.debug("Trigger fired for prepare");
256-
}
257-
} catch (final XPathException | PermissionDeniedException e) {
258-
TriggerStatePerThread.clear();
259-
throw new TriggerException(PREPARE_EXCEPTION_MESSAGE, e);
265+
266+
compiled = broker.getBrokerPool().getXQueryPool().borrowCompiledXQuery(broker, query);
267+
if (compiled == null) {
268+
context = new XQueryContext(broker.getBrokerPool());
269+
} else {
270+
context = compiled.getContext();
271+
context.prepareForReuse();
272+
}
273+
274+
if (compiled == null) {
275+
compiled = service.compile(context, query);
276+
} else {
277+
compiled.getContext().updateContext(context);
278+
context.getWatchDog().reset();
279+
}
280+
281+
declareExternalVariables(context, TriggerPhase.BEFORE, event, src, dst, isCollection);
282+
283+
//execute the XQuery
284+
//TODO : should we provide another contextSet ?
285+
final NodeSet contextSet = NodeSet.EMPTY_SET;
286+
service.execute(broker, compiled, contextSet);
287+
//TODO : should we have a special processing ?
288+
if (LOG.isDebugEnabled()) {
289+
LOG.debug("Trigger fired for prepare");
290+
}
291+
292+
} catch (final XPathException | IOException | PermissionDeniedException e) {
293+
TriggerStatePerThread.clear();
294+
throw new TriggerException(PREPARE_EXCEPTION_MESSAGE, e);
260295
} finally {
261-
context.runCleanupTasks();
296+
if (context != null) {
297+
context.runCleanupTasks();
298+
}
299+
if (compiled != null) {
300+
broker.getBrokerPool().getXQueryPool().returnCompiledXQuery(query, compiled);
301+
}
262302
}
263303
}
264304

@@ -276,33 +316,42 @@ private void finish(final TriggerEvent event, final DBBroker broker, final Txn t
276316
LOG.warn(e.getMessage());
277317
return;
278318
}
279-
280-
final XQueryContext context = new XQueryContext(broker.getBrokerPool());
281-
CompiledXQuery compiledQuery = null;
319+
320+
@Nullable CompiledXQuery compiled = null;
321+
@Nullable XQueryContext context = null;
282322
try {
283-
//compile the XQuery
284-
compiledQuery = service.compile(context, query);
285-
declareExternalVariables(context, TriggerPhase.AFTER, event, src, dst, isCollection);
323+
compiled = broker.getBrokerPool().getXQueryPool().borrowCompiledXQuery(broker, query);
324+
if (compiled == null) {
325+
context = new XQueryContext(broker.getBrokerPool());
326+
} else {
327+
context = compiled.getContext();
328+
context.prepareForReuse();
329+
}
330+
331+
if (compiled == null) {
332+
compiled = service.compile(context, query);
333+
} else {
334+
compiled.getContext().updateContext(context);
335+
context.getWatchDog().reset();
336+
}
337+
338+
declareExternalVariables(context, TriggerPhase.AFTER, event, src, dst, isCollection);
339+
340+
//execute the XQuery
341+
//TODO : should we provide another contextSet ?
342+
final NodeSet contextSet = NodeSet.EMPTY_SET;
343+
service.execute(broker, compiled, contextSet);
344+
//TODO : should we have a special processing ?
286345

287346
} catch (final XPathException | IOException | PermissionDeniedException e) {
288-
//Should never be reached
289-
LOG.error(e);
290-
}
291-
292-
//execute the XQuery
293-
try {
294-
//TODO : should we provide another contextSet ?
295-
final NodeSet contextSet = NodeSet.EMPTY_SET;
296-
service.execute(broker, compiledQuery, contextSet);
297-
//TODO : should we have a special processing ?
298-
} catch (final XPathException e) {
299-
//Should never be reached
300-
LOG.error("Error during trigger finish", e);
301-
} catch (final PermissionDeniedException e) {
302-
//Should never be reached
303-
LOG.error(e);
347+
LOG.error("Error during trigger finish", e);
304348
} finally {
305-
context.runCleanupTasks();
349+
if (context != null) {
350+
context.runCleanupTasks();
351+
}
352+
if (compiled != null) {
353+
broker.getBrokerPool().getXQueryPool().returnCompiledXQuery(query, compiled);
354+
}
306355
}
307356

308357
TriggerStatePerThread.clearIfFinished(TriggerPhase.AFTER);
@@ -314,28 +363,28 @@ private void finish(final TriggerEvent event, final DBBroker broker, final Txn t
314363

315364
private void declareExternalVariables(final XQueryContext context, final TriggerPhase phase, final TriggerEvent event, final XmldbURI src, final XmldbURI dst, final boolean isCollection) throws XPathException {
316365
//declare external variables
317-
context.declareVariable(bindingPrefix + "type", new StringValue(phase.legacyPhaseName()));
318-
context.declareVariable(bindingPrefix + "event", new StringValue(event.legacyEventName()));
366+
context.declareVariable(bindingPrefix + "type", true, new StringValue(phase.legacyPhaseName()));
367+
context.declareVariable(bindingPrefix + "event", true, new StringValue(event.legacyEventName()));
319368
if (isCollection) {
320-
context.declareVariable(bindingPrefix + "collection", new AnyURIValue(src));
369+
context.declareVariable(bindingPrefix + "collection", true, new AnyURIValue(src));
321370
} else {
322-
context.declareVariable(bindingPrefix + "collection", new AnyURIValue(src.removeLastSegment()));
371+
context.declareVariable(bindingPrefix + "collection", true, new AnyURIValue(src.removeLastSegment()));
323372
}
324-
context.declareVariable(bindingPrefix + "uri", new AnyURIValue(src));
373+
context.declareVariable(bindingPrefix + "uri", true, new AnyURIValue(src));
325374
if (dst == null) {
326-
context.declareVariable(bindingPrefix + "new-uri", Sequence.EMPTY_SEQUENCE);
375+
context.declareVariable(bindingPrefix + "new-uri", true, Sequence.EMPTY_SEQUENCE);
327376
} else {
328-
context.declareVariable(bindingPrefix + "new-uri", new AnyURIValue(dst));
377+
context.declareVariable(bindingPrefix + "new-uri", true, new AnyURIValue(dst));
329378
}
330379

331380
// For backward compatibility
332-
context.declareVariable(bindingPrefix + "eventType", new StringValue(phase.legacyPhaseName()));
333-
context.declareVariable(bindingPrefix + "triggerEvent", new StringValue(event.legacyEventName()));
381+
context.declareVariable(bindingPrefix + "eventType", true, new StringValue(phase.legacyPhaseName()));
382+
context.declareVariable(bindingPrefix + "triggerEvent", true, new StringValue(event.legacyEventName()));
334383
if (isCollection) {
335-
context.declareVariable(bindingPrefix + "collectionName", new AnyURIValue(src));
384+
context.declareVariable(bindingPrefix + "collectionName", true, new AnyURIValue(src));
336385
} else {
337-
context.declareVariable(bindingPrefix + "collectionName", new AnyURIValue(src.removeLastSegment()));
338-
context.declareVariable(bindingPrefix + "documentName", new AnyURIValue(src));
386+
context.declareVariable(bindingPrefix + "collectionName", true, new AnyURIValue(src.removeLastSegment()));
387+
context.declareVariable(bindingPrefix + "documentName", true, new AnyURIValue(src));
339388
}
340389

341390
//declare user defined parameters as external variables
@@ -344,7 +393,7 @@ private void declareExternalVariables(final XQueryContext context, final Trigger
344393
final String varName = (String) o;
345394
final String varValue = userDefinedVariables.getProperty(varName);
346395

347-
context.declareVariable(bindingPrefix + varName, new StringValue(varValue));
396+
context.declareVariable(bindingPrefix + varName, true, new StringValue(varValue));
348397
}
349398
}
350399
}
@@ -356,37 +405,48 @@ private CompiledXQuery getScript(final DBBroker broker, final Txn transaction) t
356405
if(query == null) {
357406
return null;
358407
}
359-
360-
final XQueryContext context = new XQueryContext(broker.getBrokerPool());
361-
if (query instanceof DBSource) {
362-
context.setModuleLoadPath(XmldbURI.EMBEDDED_SERVER_URI_PREFIX + ((DBSource)query).getDocumentPath().removeLastSegment().toString());
363-
}
364408

365-
CompiledXQuery compiledQuery;
409+
@Nullable CompiledXQuery compiled = null;
410+
@Nullable XQueryContext context = null;
366411
try {
412+
compiled = broker.getBrokerPool().getXQueryPool().borrowCompiledXQuery(broker, query);
413+
if (compiled == null) {
414+
context = new XQueryContext(broker.getBrokerPool());
415+
} else {
416+
context = compiled.getContext();
417+
context.prepareForReuse();
418+
}
419+
420+
if (query instanceof DBSource) {
421+
context.setModuleLoadPath(XmldbURI.EMBEDDED_SERVER_URI_PREFIX + ((DBSource)query).getDocumentPath().removeLastSegment().toString());
422+
}
423+
367424
//compile the XQuery
368-
compiledQuery = service.compile(context, query);
425+
if (compiled == null) {
426+
compiled = service.compile(context, query);
427+
} else {
428+
compiled.getContext().updateContext(context);
429+
context.getWatchDog().reset();
430+
}
369431

370432
//declare user defined parameters as external variables
371433
if (userDefinedVariables != null) {
372434
for (final Object o : userDefinedVariables.keySet()) {
373435
final String varName = (String) o;
374436
final String varValue = userDefinedVariables.getProperty(varName);
375437

376-
context.declareVariable(bindingPrefix + varName, new StringValue(varValue));
438+
context.declareVariable(bindingPrefix + varName, true, new StringValue(varValue));
377439
}
378440
}
379-
380-
//reset & prepareForExecution for execution
381-
compiledQuery.reset();
382441

383-
context.getWatchDog().reset();
384-
385-
//do any preparation before execution
386-
context.prepareForExecution();
387-
388-
return compiledQuery;
442+
return compiled;
389443
} catch(final XPathException | IOException | PermissionDeniedException e) {
444+
if (context != null) {
445+
context.runCleanupTasks();
446+
}
447+
if (compiled != null) {
448+
broker.getBrokerPool().getXQueryPool().returnCompiledXQuery(query, compiled);
449+
}
390450
LOG.warn(e.getMessage(), e);
391451
throw new TriggerException(PREPARE_EXCEPTION_MESSAGE, e);
392452
}
@@ -406,23 +466,18 @@ private void execute(final TriggerPhase phase, final TriggerEvent event, final D
406466
LOG.debug("Execute: {} {}({}): {}", phase, event, src, getClass().getSimpleName());
407467
}
408468

409-
final CompiledXQuery compiledQuery;
469+
@Nullable CompiledXQuery compiled = null;
470+
@Nullable XQueryContext context = null;
410471
try {
411-
compiledQuery = getScript(broker, transaction);
412-
if (compiledQuery == null) {
472+
compiled = getScript(broker, transaction);
473+
if (compiled == null) {
413474
// NOTE: can occur if there is no such XQueryTrigger library module available in the database
414475
TriggerStatePerThread.clearIfFinished(phase);
415476
return;
416477
}
417-
} catch (final TriggerException e) {
418-
TriggerStatePerThread.clear();
419-
throw e;
420-
}
421-
422-
final XQueryContext context = compiledQuery.getContext();
478+
context = compiled.getContext();
423479

424-
//execute the XQuery
425-
try {
480+
//execute the XQuery
426481
final int nParams;
427482
if (dst != null) {
428483
nParams = 2;
@@ -443,13 +498,18 @@ private void execute(final TriggerPhase phase, final TriggerEvent event, final D
443498
args.add(new LiteralValue(context, new AnyURIValue(src)));
444499
}
445500

446-
service.execute(broker, compiledQuery, Tuple(functionName, args, Optional.empty()), null, null, true);
501+
service.execute(broker, compiled, Tuple(functionName, args, Optional.empty()), null, null, true);
502+
503+
} catch (final TriggerException e) {
504+
TriggerStatePerThread.clear();
505+
throw e;
506+
447507
} catch (final XPathException | PermissionDeniedException e) {
448508
// if the exception just indicates that there is no function in the trigger to call, then we can just log and return
449509
if (e instanceof XPathException xpe) {
450510
if (xpe.getErrorCode() == ErrorCodes.EXXQDY0005 || xpe.getErrorCode() == ErrorCodes.EXXQDY0006) {
451511
if (LOG.isDebugEnabled()) {
452-
LOG.debug("No such function '" + functionName + "' in XQueryTrigger: " + compiledQuery.getSource());
512+
LOG.debug("No such function '" + functionName + "' in XQueryTrigger: " + compiled.getSource());
453513
}
454514
TriggerStatePerThread.clearIfFinished(phase);
455515
return;
@@ -459,8 +519,12 @@ private void execute(final TriggerPhase phase, final TriggerEvent event, final D
459519
TriggerStatePerThread.clear();
460520
throw new TriggerException(PREPARE_EXCEPTION_MESSAGE, e);
461521
} finally {
462-
compiledQuery.reset();
463-
context.runCleanupTasks();
522+
if (context != null) {
523+
context.runCleanupTasks();
524+
}
525+
if (compiled != null) {
526+
broker.getBrokerPool().getXQueryPool().returnCompiledXQuery(compiled.getSource(), compiled);
527+
}
464528
}
465529

466530
TriggerStatePerThread.clearIfFinished(phase);

0 commit comments

Comments
 (0)