diff --git a/exist-core/pom.xml b/exist-core/pom.xml
index fa38a45d04..af30839b96 100644
--- a/exist-core/pom.xml
+++ b/exist-core/pom.xml
@@ -1257,6 +1257,7 @@
src/test/java/org/exist/xquery/functions/validate/JingXsdTest.java
src/main/java/org/exist/xquery/functions/validation/Jaxp.java
src/test/java/org/exist/xquery/functions/xmldb/DbStore2Test.java
+ src/main/java/org/exist/xquery/functions/xmldb/XMLDBModule.java
src/main/java/org/exist/xquery/functions/xmldb/XMLDBStore.java
src/main/java/org/exist/xquery/functions/xmldb/XMLDBXUpdate.java
src/test/java/org/exist/xquery/functions/xquery3/TryCatchTest.java
@@ -1948,6 +1949,7 @@
src/test/java/org/exist/xquery/functions/xmldb/AbstractXMLDBTest.java
src/test/java/org/exist/xquery/functions/xmldb/DbStore2Test.java
src/test/java/org/exist/xquery/functions/xmldb/XMLDBAuthenticateTest.java
+ src/main/java/org/exist/xquery/functions/xmldb/XMLDBModule.java
src/main/java/org/exist/xquery/functions/xmldb/XMLDBStore.java
src/test/java/org/exist/xquery/functions/xmldb/XMLDBStoreTest.java
src/main/java/org/exist/xquery/functions/xmldb/XMLDBXUpdate.java
diff --git a/exist-core/src/main/java/org/exist/http/RESTServer.java b/exist-core/src/main/java/org/exist/http/RESTServer.java
index 7b0670fe45..163d27e3d7 100644
--- a/exist-core/src/main/java/org/exist/http/RESTServer.java
+++ b/exist-core/src/main/java/org/exist/http/RESTServer.java
@@ -936,13 +936,12 @@ public void doPost(final DBBroker broker, final Txn transaction, final HttpServl
}
final XUpdateProcessor processor = new XUpdateProcessor(broker, docs);
+ root.toSAX(broker, processor, new Properties());
+ final List modifications = processor.getModifications();
long mods = 0;
- try(final Reader reader = new StringReader(content)) {
- final Modification modifications[] = processor.parse(new InputSource(reader));
- for (Modification modification : modifications) {
- mods += modification.process(transaction);
- broker.flush();
- }
+ for (final Modification modification : modifications) {
+ mods += modification.process(transaction);
+ broker.flush();
}
// FD : Returns an XML doc
diff --git a/exist-core/src/main/java/org/exist/xquery/functions/xmldb/XMLDBModule.java b/exist-core/src/main/java/org/exist/xquery/functions/xmldb/XMLDBModule.java
index f328610ab7..3394ea4403 100644
--- a/exist-core/src/main/java/org/exist/xquery/functions/xmldb/XMLDBModule.java
+++ b/exist-core/src/main/java/org/exist/xquery/functions/xmldb/XMLDBModule.java
@@ -1,4 +1,28 @@
/*
+ * Elemental
+ * Copyright (C) 2024, Evolved Binary Ltd
+ *
+ * admin@evolvedbinary.com
+ * https://www.evolvedbinary.com | https://www.elemental.xyz
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
+ * The original license header is included below.
+ *
+ * =====================================================================
+ *
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
@@ -64,7 +88,8 @@ public class XMLDBModule extends AbstractInternalModule {
new FunctionDef(XMLDBLoadFromPattern.signatures[1], XMLDBLoadFromPattern.class),
new FunctionDef(XMLDBLoadFromPattern.signatures[2], XMLDBLoadFromPattern.class),
new FunctionDef(XMLDBLoadFromPattern.signatures[3], XMLDBLoadFromPattern.class),
- new FunctionDef(XMLDBXUpdate.signature, XMLDBXUpdate.class),
+ new FunctionDef(XMLDBXUpdate.FS_UPDATE[0], XMLDBXUpdate.class),
+ new FunctionDef(XMLDBXUpdate.FS_UPDATE[1], XMLDBXUpdate.class),
new FunctionDef(XMLDBCopy.FS_COPY_COLLECTION[0], XMLDBCopy.class),
new FunctionDef(XMLDBCopy.FS_COPY_COLLECTION[1], XMLDBCopy.class),
new FunctionDef(XMLDBCopy.FS_COPY_RESOURCE[0], XMLDBCopy.class),
diff --git a/exist-core/src/main/java/org/exist/xquery/functions/xmldb/XMLDBXUpdate.java b/exist-core/src/main/java/org/exist/xquery/functions/xmldb/XMLDBXUpdate.java
index 2953753806..462b49cf93 100644
--- a/exist-core/src/main/java/org/exist/xquery/functions/xmldb/XMLDBXUpdate.java
+++ b/exist-core/src/main/java/org/exist/xquery/functions/xmldb/XMLDBXUpdate.java
@@ -45,85 +45,117 @@
*/
package org.exist.xquery.functions.xmldb;
-import org.apache.commons.io.output.StringBuilderWriter;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import org.exist.dom.QName;
-import org.exist.util.serializer.DOMSerializer;
-import org.exist.xquery.Cardinality;
+import org.exist.EXistException;
+import org.exist.collections.Collection;
+import org.exist.dom.persistent.*;
+import org.exist.security.PermissionDeniedException;
+import org.exist.storage.lock.Lock;
+import org.exist.util.LockException;
+import org.exist.xmldb.XmldbURI;
+import org.exist.xquery.BasicFunction;
import org.exist.xquery.FunctionSignature;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
-import org.exist.xquery.value.FunctionReturnSequenceType;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.IntegerValue;
-import org.exist.xquery.value.NodeValue;
+import org.exist.xquery.value.Item;
import org.exist.xquery.value.Sequence;
-import org.exist.xquery.value.SequenceType;
import org.exist.xquery.value.Type;
-import org.xmldb.api.base.Collection;
-import org.xmldb.api.base.XMLDBException;
-import org.xmldb.api.modules.XUpdateQueryService;
+import org.exist.xupdate.Modification;
+import org.exist.xupdate.XUpdateProcessor;
+import org.xml.sax.SAXException;
+
+import javax.annotation.Nullable;
+import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.TransformerException;
+import java.util.List;
import java.util.Properties;
+import static org.exist.xquery.FunctionDSL.*;
+import static org.exist.xquery.functions.xmldb.XMLDBModule.functionSignatures;
+
/**
- *
- * @author wolf
- *
+ * @author Adam Retter
*/
-public class XMLDBXUpdate extends XMLDBAbstractCollectionManipulator
-{
- protected static final Logger logger = LogManager.getLogger(XMLDBXUpdate.class);
-
- public final static FunctionSignature signature = new FunctionSignature(
- new QName("update", XMLDBModule.NAMESPACE_URI, XMLDBModule.PREFIX),
- "Processes an XUpdate request, $modifications, against a collection $collection-uri. "
- + XMLDBModule.COLLECTION_URI
- + "The modifications are passed in a "
- + "document conforming to the XUpdate specification. "
- + "http://rx4rdf.liminalzone.org/xupdate-wd.html#N1a32e0"
- + "The function returns the number of modifications caused by the XUpdate.",
- new SequenceType[]{
- new FunctionParameterSequenceType("collection-uri", Type.STRING, Cardinality.EXACTLY_ONE, "The collection URI"),
- new FunctionParameterSequenceType("modifications", Type.NODE, Cardinality.EXACTLY_ONE, "The XUpdate modifications to be processed")},
- new FunctionReturnSequenceType(Type.INTEGER, Cardinality.EXACTLY_ONE, "the number of modifications, as xs:integer, caused by the XUpdate"));
-
- public XMLDBXUpdate(XQueryContext context) {
- super(context, signature);
+public class XMLDBXUpdate extends BasicFunction {
+ private static final FunctionParameterSequenceType FS_PARAM_COLLECTION_URI = param("collection-uri", Type.STRING, "The collection URI");
+ private static final FunctionParameterSequenceType FS_PARAM_DOCUMENT_URI = param("document-uri", Type.STRING, "The document URI");
+ private static final FunctionParameterSequenceType FS_PARAM_MODIFICATIONS = param("modifications", Type.NODE, "The XUpdate modifications to be processed");
+
+ private static final String FS_UPDATE_NAME = "update";
+ static final FunctionSignature[] FS_UPDATE = functionSignatures(
+ FS_UPDATE_NAME,
+ "Processes an XUpdate request, $modifications, against a collection $collection-uri. "
+ + XMLDBModule.COLLECTION_URI
+ + "The modifications are passed in a "
+ + "document conforming to the XUpdate specification. "
+ + "https://xmldb-org.sourceforge.net/xupdate/xupdate-wd.html"
+ + "The function returns the number of modifications caused by the XUpdate.",
+ returns(Type.INTEGER, "The number of modifications, as an xs:integer, caused by the XUpdate"),
+ arities(
+ arity(
+ FS_PARAM_COLLECTION_URI,
+ FS_PARAM_MODIFICATIONS
+ ),
+ arity(
+ FS_PARAM_COLLECTION_URI,
+ FS_PARAM_DOCUMENT_URI,
+ FS_PARAM_MODIFICATIONS
+ )
+ )
+ );
+
+ public XMLDBXUpdate(final XQueryContext context, final FunctionSignature functionSignature) {
+ super(context, functionSignature);
}
- /* (non-Javadoc)
- * @see org.exist.xquery.BasicFunction#eval(org.exist.xquery.value.Sequence[], org.exist.xquery.value.Sequence)
- */
- public Sequence evalWithCollection(Collection c, Sequence[] args, Sequence contextSequence)
- throws XPathException {
- final NodeValue data = (NodeValue) args[1].itemAt(0);
- final String xupdate;
- try (final StringBuilderWriter writer = new StringBuilderWriter()) {
- final Properties properties = new Properties();
- properties.setProperty(OutputKeys.INDENT, "yes");
- final DOMSerializer serializer = new DOMSerializer(writer, properties);
- serializer.serialize(data.getNode());
- xupdate = writer.toString();
- } catch(final TransformerException e) {
- logger.debug("Exception while serializing XUpdate document", e);
- throw new XPathException(this, "Exception while serializing XUpdate document: " + e.getMessage(), e);
- }
-
- long modifications = 0;
- try {
- final XUpdateQueryService service = (XUpdateQueryService)c.getService("XUpdateQueryService", "1.0");
- logger.debug("Processing XUpdate request: {}", xupdate);
- modifications = service.update(xupdate);
- } catch(final XMLDBException e) {
- throw new XPathException(this, "Exception while processing xupdate: " + e.getMessage(), e);
- }
-
- context.getRootExpression().resetState(false);
- return new IntegerValue(this, modifications);
+ @Override
+ public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException {
+ final String collectionUri = args[0].itemAt(0).getStringValue();
+ @Nullable final String documentUri;
+ final Item modifications;
+ if (args.length == 3) {
+ documentUri = args[1].itemAt(0).getStringValue();
+ modifications = args[2].itemAt(0);
+ } else {
+ documentUri = null;
+ modifications = args[1].itemAt(0);
+ }
+
+ final MutableDocumentSet documentSet = new DefaultDocumentSet();
+ try (final Collection collection = context.getBroker().openCollection(XmldbURI.create(collectionUri), Lock.LockMode.READ_LOCK)) {
+ if (documentUri == null) {
+ collection.allDocs(context.getBroker(), documentSet, true);
+
+ } else {
+ try (final LockedDocument lockedDocument = collection.getDocumentWithLock(context.getBroker(), XmldbURI.create(documentUri), Lock.LockMode.READ_LOCK)) {
+
+ // NOTE: early release of Collection lock inline with Asymmetrical Locking scheme
+ collection.close();
+
+ final DocumentImpl doc = lockedDocument == null ? null : lockedDocument.getDocument();
+ if (doc == null) {
+ throw new XPathException(this, "Resource not found: " + documentUri);
+ }
+ documentSet.add(doc);
+ }
+ }
+
+ final XUpdateProcessor processor = new XUpdateProcessor(context.getBroker(), documentSet);
+ modifications.toSAX(context.getBroker(), processor, new Properties());
+ final List modificationList = processor.getModifications();
+ long mods = 0;
+ for (final Modification modification : modificationList) {
+ mods += modification.process(context.getBroker().getCurrentTransaction());
+ context.getBroker().flush();
+ }
+
+ context.getRootExpression().resetState(false);
+
+ return new IntegerValue(this, mods);
+
+ } catch (final PermissionDeniedException | LockException | EXistException | ParserConfigurationException | SAXException e) {
+ throw new XPathException(this, e);
+ }
}
}
diff --git a/exist-core/src/main/java/org/exist/xupdate/XUpdateProcessor.java b/exist-core/src/main/java/org/exist/xupdate/XUpdateProcessor.java
index 98fd444d06..911a2d3cd9 100644
--- a/exist-core/src/main/java/org/exist/xupdate/XUpdateProcessor.java
+++ b/exist-core/src/main/java/org/exist/xupdate/XUpdateProcessor.java
@@ -867,16 +867,20 @@ public void startDTD(final String name, final String publicId, final String syst
public void startEntity(final String name) throws SAXException {
}
+ public List getModifications() {
+ return modifications;
+ }
+
public void reset() {
- if (this.whiteSpaceHandling != null) {
- this.whiteSpaceHandling.clear();
- }
- this.whiteSpaceHandlingIdx = 0;
+ if (this.whiteSpaceHandling != null) {
+ this.whiteSpaceHandling.clear();
+ }
+ this.whiteSpaceHandlingIdx = 0;
this.inModification = false;
this.inAttribute = false;
this.modification = null;
this.builder.reset();
- this.doc = null;
+ this.doc = null;
this.contents = null;
if (this.stack != null) {
this.stack.clear();