Skip to content

Commit 6e80c9b

Browse files
authored
Merge pull request #4996 from evolvedbinary/hotfix/restxq-circular-imports
Fixes to eXist-db regarding circular imports
2 parents a65d0b0 + 78c34d2 commit 6e80c9b

File tree

19 files changed

+1371
-235
lines changed

19 files changed

+1371
-235
lines changed

exist-core/pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,17 @@
514514
<artifactId>ant</artifactId>
515515
</dependency>
516516

517+
<dependency>
518+
<groupId>org.jgrapht</groupId>
519+
<artifactId>jgrapht-core</artifactId>
520+
<version>1.5.2</version>
521+
</dependency>
522+
523+
<dependency>
524+
<groupId>org.jgrapht</groupId>
525+
<artifactId>jgrapht-opt</artifactId>
526+
<version>1.5.2</version>
527+
</dependency>
517528

518529
<dependency>
519530
<groupId>junit</groupId>

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -760,8 +760,6 @@ public interface Context {
760760
*/
761761
boolean tailRecursiveCall(FunctionSignature signature);
762762

763-
void mapModule(String namespace, XmldbURI uri);
764-
765763
/**
766764
* Import one or more library modules into the function signatures and in-scope variables of the importing module.
767765
*
@@ -783,7 +781,7 @@ public interface Context {
783781
* XQST0070
784782
* XQST0088
785783
*/
786-
Module[] importModule(@Nullable String namespaceURI, @Nullable String prefix, @Nullable AnyURIValue[] locationHints) throws XPathException;
784+
@Nullable Module[] importModule(@Nullable String namespaceURI, @Nullable String prefix, @Nullable AnyURIValue[] locationHints) throws XPathException;
787785

788786
/**
789787
* Returns the static location mapped to an XQuery source module, if known.

exist-core/src/main/java/org/exist/storage/NativeBroker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ public NativeBroker(final BrokerPool pool, final Configuration config) throws EX
248248
this.indexConfiguration = (IndexSpec) config.getProperty(Indexer.PROPERTY_INDEXER_CONFIG);
249249
this.xmlSerializerPool = new XmlSerializerPool(this, config, 5);
250250

251+
pushSubject(pool.getSecurityManager().getSystemSubject());
251252
try {
252-
pushSubject(pool.getSecurityManager().getSystemSubject());
253253
//TODO : refactor so that we can,
254254
//1) customize the different properties (file names, cache settings...)
255255
//2) have a consistent READ-ONLY behaviour (based on *mandatory* files ?)

exist-core/src/main/java/org/exist/util/serializer/XQuerySerializer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ private void serializeXML(final Sequence sequence, final int start, final int ho
105105
private void serializeJSON(final Sequence sequence, final long compilationTime, final long executionTime) throws SAXException, XPathException {
106106
// backwards compatibility: if the sequence contains a single element, we assume
107107
// it should be transformed to JSON following the rules of the old JSON writer
108-
if (sequence.hasOne() && Type.subTypeOf(sequence.getItemType(), Type.ELEMENT)) {
109-
serializeXML(sequence, 1, sequence.getItemCount(), false, false, compilationTime, executionTime);
108+
if (sequence.hasOne() && (Type.subTypeOf(sequence.getItemType(), Type.DOCUMENT) || Type.subTypeOf(sequence.getItemType(), Type.ELEMENT))) {
109+
serializeXML(sequence, 1, 1, false, false, compilationTime, executionTime);
110110
} else {
111111
JSONSerializer serializer = new JSONSerializer(broker, outputProperties);
112112
serializer.serialize(sequence, writer);

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

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.exist.dom.memtree.MemTreeBuilder;
3030
import org.exist.security.Subject;
3131
import org.exist.storage.UpdateListener;
32-
import org.exist.util.Configuration;
3332
import org.exist.util.FileUtils;
3433
import org.exist.xmldb.XmldbURI;
3534
import org.exist.xquery.value.AnyURIValue;
@@ -49,29 +48,27 @@
4948

5049

5150
/**
52-
* Subclass of {@link org.exist.xquery.XQueryContext} for
53-
* imported modules.
51+
* Subclass of {@link org.exist.xquery.XQueryContext} for imported modules.
5452
*
5553
* @author wolf
54+
* @author <a href="mailto:[email protected]">Adam Retter</a>
5655
*/
5756
public class ModuleContext extends XQueryContext {
5857

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

6160
private XQueryContext parentContext;
62-
private String modulePrefix;
6361
private String moduleNamespace;
62+
private String modulePrefix;
6463
private final String location;
6564

66-
public ModuleContext(final XQueryContext parentContext, final String modulePrefix, final String moduleNamespace,
67-
final String location) {
65+
public ModuleContext(final XQueryContext parentContext, final String moduleNamespace, final String modulePrefix, final String location) {
6866
super(parentContext != null ? parentContext.db : null,
6967
parentContext != null ? parentContext.getConfiguration() : null,
7068
null,
7169
false);
72-
73-
this.modulePrefix = modulePrefix;
7470
this.moduleNamespace = moduleNamespace;
71+
this.modulePrefix = modulePrefix;
7572
this.location = location;
7673

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

98+
@Override
99+
protected void addModuleVertex(final ModuleVertex moduleVertex) {
100+
getRootContext().addModuleVertex(moduleVertex);
101+
}
102+
103+
protected boolean hasModuleVertex(final ModuleVertex moduleVertex) {
104+
return getRootContext().hasModuleVertex(moduleVertex);
105+
}
106+
107+
@Override
108+
protected void addModuleEdge(final ModuleVertex source, final ModuleVertex sink) {
109+
getRootContext().addModuleEdge(source, sink);
110+
}
111+
112+
@Override
113+
protected boolean hasModulePath(final ModuleVertex source, final ModuleVertex sink) {
114+
return getRootContext().hasModulePath(source, sink);
115+
}
116+
117+
@Override
118+
public @Nullable Module[] importModule(@Nullable String namespaceURI, @Nullable String prefix, @Nullable AnyURIValue[] locationHints) throws XPathException {
119+
final ModuleVertex thisModuleVertex = new ModuleVertex(moduleNamespace, location);
120+
121+
for (final AnyURIValue locationHint : locationHints) {
122+
final ModuleVertex imporedModuleVertex = new ModuleVertex(namespaceURI, locationHint.toString());
123+
124+
if (!hasModuleVertex(imporedModuleVertex)) {
125+
addModuleVertex(imporedModuleVertex);
126+
} else {
127+
// Check if there is already a path from the imported module to this module
128+
if (getXQueryVersion() == 10 && namespaceURI != null && locationHints != null && hasModulePath(imporedModuleVertex, thisModuleVertex)) {
129+
throw new XPathException(ErrorCodes.XQST0093, "Detected cyclic import between modules: " + getModuleNamespace() + " at: " + getLocation() + ", and: " + namespaceURI + " at: " + locationHint.toString());
130+
}
131+
}
132+
133+
if (!hasModuleVertex(thisModuleVertex)) {
134+
// 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
135+
addModuleVertex(thisModuleVertex);
136+
}
137+
138+
addModuleEdge(thisModuleVertex, imporedModuleVertex);
139+
}
140+
141+
return super.importModule(namespaceURI, prefix, locationHints);
142+
}
143+
144+
@Override
145+
protected @Nullable Module importModuleFromLocation(final String namespaceURI, @Nullable final String prefix, final AnyURIValue locationHint) throws XPathException {
146+
// guard against self-recursive import - see: https://github.com/eXist-db/exist/issues/3448
147+
if (moduleNamespace.equals(namespaceURI) && location.equals(locationHint.toString())) {
148+
final StringBuilder builder = new StringBuilder("The XQuery Library Module '");
149+
builder.append(namespaceURI);
150+
builder.append("'");
151+
if (locationHint != null) {
152+
builder.append(" at '");
153+
builder.append(location);
154+
builder.append("'");
155+
}
156+
builder.append(" has invalidly attempted to import itself; this will be skipped!");
157+
LOG.warn(builder.toString());
158+
159+
return null;
160+
}
161+
162+
return super.importModuleFromLocation(namespaceURI, prefix, locationHint);
163+
}
164+
101165
@Override
102166
protected void setModulesChanged() {
103167
parentContext.setModulesChanged();
@@ -176,7 +240,7 @@ public void updateContext(final XQueryContext from) {
176240

177241
@Override
178242
public XQueryContext copyContext() {
179-
final ModuleContext ctx = new ModuleContext(parentContext, modulePrefix, moduleNamespace, location);
243+
final ModuleContext ctx = new ModuleContext(parentContext, moduleNamespace, modulePrefix, location);
180244
copyFields(ctx);
181245
try {
182246
ctx.declareNamespace(modulePrefix, moduleNamespace);

0 commit comments

Comments
 (0)