Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to eXist-db regarding circular imports #4996

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d831105
[ignore] Code cleanup
adamretter Jul 28, 2023
5e092b9
[bugfix] No need to call addModule here as the caller calls it also
adamretter Jul 28, 2023
824127b
[bugfix] Prevent a Library Module from importing itself
adamretter Jul 28, 2023
0df83fd
[test] jetty dependencies and system properties
alanpaxton May 11, 2023
452d9fa
[bugfix] Use the correct Namespace for XQuery Serialization spec
adamretter Jul 28, 2023
24d99ee
[bugfix] Reinstate RESTXQ test for JSON Media Type
adamretter Jul 28, 2023
bb7b9d2
[test] Add further RESTXQ Media Type acceptance tests
adamretter Jul 28, 2023
bffd3e7
[test] Add further tests for RESTXQ Media Type serialization
adamretter Jul 28, 2023
74ef58e
[bugfix] JSON Serialization should also handle Document nodes
adamretter Jul 28, 2023
b2239b5
[refactor] Rename RESTXQ Media Type tests
adamretter Jul 28, 2023
3776e2c
[test] Test for Self Import Circular Dependency. See https://github.c…
adamretter Jul 28, 2023
5eb6d97
[test] Add XQuery 1.0 and XQuery 3.1 tests for cyclic library module …
adamretter Jul 29, 2023
dae3352
[test] Add RESTXQ tests for circular dependencies between XQuery Libr…
adamretter Jul 28, 2023
9e54ea0
[test] Add explicit namespace declaration
adamretter Jul 29, 2023
4b92888
[refactor] Swap arguments for internal consistency purposes
adamretter Jul 29, 2023
15e62ea
[bugfix] Cyclic dependency import detection for XQuery 1.0 (i.e. XQST…
adamretter Jul 29, 2023
6679db0
[optimisation] Lazy initialisation of Modules Dependency Graph
adamretter Jul 31, 2023
78c34d2
[bugfix] Allow for one ExistXqueryRegistry per BrokerPool instance
adamretter Jul 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions exist-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,17 @@
<artifactId>ant</artifactId>
</dependency>

<dependency>
<groupId>org.jgrapht</groupId>
<artifactId>jgrapht-core</artifactId>
<version>1.5.2</version>
</dependency>

<dependency>
<groupId>org.jgrapht</groupId>
<artifactId>jgrapht-opt</artifactId>
<version>1.5.2</version>
</dependency>

<dependency>
<groupId>junit</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -760,8 +760,6 @@ public interface Context {
*/
boolean tailRecursiveCall(FunctionSignature signature);

void mapModule(String namespace, XmldbURI uri);

/**
* Import one or more library modules into the function signatures and in-scope variables of the importing module.
*
Expand All @@ -783,7 +781,7 @@ public interface Context {
* XQST0070
* XQST0088
*/
Module[] importModule(@Nullable String namespaceURI, @Nullable String prefix, @Nullable AnyURIValue[] locationHints) throws XPathException;
@Nullable Module[] importModule(@Nullable String namespaceURI, @Nullable String prefix, @Nullable AnyURIValue[] locationHints) throws XPathException;

/**
* Returns the static location mapped to an XQuery source module, if known.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ public NativeBroker(final BrokerPool pool, final Configuration config) throws EX
this.indexConfiguration = (IndexSpec) config.getProperty(Indexer.PROPERTY_INDEXER_CONFIG);
this.xmlSerializerPool = new XmlSerializerPool(this, config, 5);

pushSubject(pool.getSecurityManager().getSystemSubject());
try {
pushSubject(pool.getSecurityManager().getSystemSubject());
//TODO : refactor so that we can,
//1) customize the different properties (file names, cache settings...)
//2) have a consistent READ-ONLY behaviour (based on *mandatory* files ?)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ private void serializeXML(final Sequence sequence, final int start, final int ho
private void serializeJSON(final Sequence sequence, final long compilationTime, final long executionTime) throws SAXException, XPathException {
// backwards compatibility: if the sequence contains a single element, we assume
// it should be transformed to JSON following the rules of the old JSON writer
if (sequence.hasOne() && Type.subTypeOf(sequence.getItemType(), Type.ELEMENT)) {
serializeXML(sequence, 1, sequence.getItemCount(), false, false, compilationTime, executionTime);
if (sequence.hasOne() && (Type.subTypeOf(sequence.getItemType(), Type.DOCUMENT) || Type.subTypeOf(sequence.getItemType(), Type.ELEMENT))) {
serializeXML(sequence, 1, 1, false, false, compilationTime, executionTime);
} else {
JSONSerializer serializer = new JSONSerializer(broker, outputProperties);
serializer.serialize(sequence, writer);
Expand Down
82 changes: 73 additions & 9 deletions exist-core/src/main/java/org/exist/xquery/ModuleContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import org.exist.dom.memtree.MemTreeBuilder;
import org.exist.security.Subject;
import org.exist.storage.UpdateListener;
import org.exist.util.Configuration;
import org.exist.util.FileUtils;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.value.AnyURIValue;
Expand All @@ -49,29 +48,27 @@


/**
* Subclass of {@link org.exist.xquery.XQueryContext} for
* imported modules.
* Subclass of {@link org.exist.xquery.XQueryContext} for imported modules.
*
* @author wolf
* @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
*/
public class ModuleContext extends XQueryContext {

private static final Logger LOG = LogManager.getLogger(ModuleContext.class);

private XQueryContext parentContext;
private String modulePrefix;
private String moduleNamespace;
private String modulePrefix;
private final String location;

public ModuleContext(final XQueryContext parentContext, final String modulePrefix, final String moduleNamespace,
final String location) {
public ModuleContext(final XQueryContext parentContext, final String moduleNamespace, final String modulePrefix, final String location) {
super(parentContext != null ? parentContext.db : null,
parentContext != null ? parentContext.getConfiguration() : null,
null,
false);

this.modulePrefix = modulePrefix;
this.moduleNamespace = moduleNamespace;
this.modulePrefix = modulePrefix;
this.location = location;

setParentContext(parentContext);
Expand All @@ -98,6 +95,73 @@ public void setModuleNamespace(final String prefix, final String namespaceURI) {
this.moduleNamespace = namespaceURI;
}

@Override
protected void addModuleVertex(final ModuleVertex moduleVertex) {
getRootContext().addModuleVertex(moduleVertex);
}

protected boolean hasModuleVertex(final ModuleVertex moduleVertex) {
return getRootContext().hasModuleVertex(moduleVertex);
}

@Override
protected void addModuleEdge(final ModuleVertex source, final ModuleVertex sink) {
getRootContext().addModuleEdge(source, sink);
}

@Override
protected boolean hasModulePath(final ModuleVertex source, final ModuleVertex sink) {
return getRootContext().hasModulePath(source, sink);
}

@Override
public @Nullable Module[] importModule(@Nullable String namespaceURI, @Nullable String prefix, @Nullable AnyURIValue[] locationHints) throws XPathException {
final ModuleVertex thisModuleVertex = new ModuleVertex(moduleNamespace, location);

for (final AnyURIValue locationHint : locationHints) {
final ModuleVertex imporedModuleVertex = new ModuleVertex(namespaceURI, locationHint.toString());

if (!hasModuleVertex(imporedModuleVertex)) {
addModuleVertex(imporedModuleVertex);
} else {
// Check if there is already a path from the imported module to this module
if (getXQueryVersion() == 10 && namespaceURI != null && locationHints != null && hasModulePath(imporedModuleVertex, thisModuleVertex)) {
throw new XPathException(ErrorCodes.XQST0093, "Detected cyclic import between modules: " + getModuleNamespace() + " at: " + getLocation() + ", and: " + namespaceURI + " at: " + locationHint.toString());
}
}

if (!hasModuleVertex(thisModuleVertex)) {
// NOTE(AR) may occur when the actual module has a different namespace from that of the `import module namespace`... will later raise an XQST0047 error
addModuleVertex(thisModuleVertex);
}

addModuleEdge(thisModuleVertex, imporedModuleVertex);
}

return super.importModule(namespaceURI, prefix, locationHints);
}

@Override
protected @Nullable Module importModuleFromLocation(final String namespaceURI, @Nullable final String prefix, final AnyURIValue locationHint) throws XPathException {
// guard against self-recursive import - see: https://github.com/eXist-db/exist/issues/3448
if (moduleNamespace.equals(namespaceURI) && location.equals(locationHint.toString())) {
final StringBuilder builder = new StringBuilder("The XQuery Library Module '");
builder.append(namespaceURI);
builder.append("'");
if (locationHint != null) {
builder.append(" at '");
builder.append(location);
builder.append("'");
}
builder.append(" has invalidly attempted to import itself; this will be skipped!");
LOG.warn(builder.toString());

return null;
}

return super.importModuleFromLocation(namespaceURI, prefix, locationHint);
}

@Override
protected void setModulesChanged() {
parentContext.setModulesChanged();
Expand Down Expand Up @@ -176,7 +240,7 @@ public void updateContext(final XQueryContext from) {

@Override
public XQueryContext copyContext() {
final ModuleContext ctx = new ModuleContext(parentContext, modulePrefix, moduleNamespace, location);
final ModuleContext ctx = new ModuleContext(parentContext, moduleNamespace, modulePrefix, location);
copyFields(ctx);
try {
ctx.declareNamespace(modulePrefix, moduleNamespace);
Expand Down
Loading
Loading